android/Bootstrap/src/org/libreoffice/kit/DirectBufferAllocator.java | 52 android/Bootstrap/src/org/libreoffice/kit/LibreOfficeKit.java | 7 android/experimental/LOAndroid3/src/java/org/libreoffice/DirectBufferAllocator.java | 33 android/experimental/LOAndroid3/src/java/org/libreoffice/LOEvent.java | 6 android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java | 16 android/experimental/LOAndroid3/src/java/org/libreoffice/LibreOfficeMainActivity.java | 26 android/experimental/LOAndroid3/src/java/org/libreoffice/TileProviderFactory.java | 31 android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/BufferedCairoImage.java | 2 android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/CheckerboardImage.java | 2 android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/DisplayPortCalculator.java | 755 ++++++++++ android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/DisplayPortMetrics.java | 58 android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/DrawTimingQueue.java | 95 + android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/FlexibleGLSurfaceView.java | 196 -- android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/GLController.java | 77 - android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/GLThread.java | 3 android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/GeckoLayerClient.java | 263 +-- android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/LayerController.java | 316 +--- android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/LayerRenderer.java | 223 +- android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/LayerView.java | 247 ++- android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/MultiTileLayer.java | 9 android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/ScreenshotLayer.java | 2 android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/ScrollbarLayer.java | 2 android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/TextLayer.java | 2 android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/TileLayer.java | 11 android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/TouchEventHandler.java | 325 ++++ android/experimental/LOAndroid3/src/java/org/mozilla/gecko/ui/PanZoomController.java | 294 ++- desktop/source/lib/lokandroid.cxx | 28 27 files changed, 2153 insertions(+), 928 deletions(-)
New commits: commit 0ab9ddaf4d67e968ad22ec296fbeda02ce1a468d Author: Tomaž Vajngerl <tomaz.vajng...@collabora.com> Date: Wed Sep 24 20:41:54 2014 +0200 android: LayerRenderer - use highp and flip in vertex shader Change-Id: Ia517b0d94fdfb3f8fdd9b9c383c8fb337173932c 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 5146b22..34c5c61 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 @@ -99,6 +99,11 @@ public class LayerRenderer implements GLSurfaceView.Renderer { // The shaders run on the GPU directly, the vertex shader is only applying the // matrix transform detailed above + + // Note we flip the y-coordinate in the vertex shader from a + // coordinate system with (0,0) in the top left to one with (0,0) in + // the bottom left. + public static final String DEFAULT_VERTEX_SHADER = "uniform mat4 uTMatrix;\n" + "attribute vec4 vPosition;\n" + @@ -106,18 +111,21 @@ public class LayerRenderer implements GLSurfaceView.Renderer { "varying vec2 vTexCoord;\n" + "void main() {\n" + " gl_Position = uTMatrix * vPosition;\n" + - " vTexCoord = aTexCoord;\n" + + " vTexCoord.x = aTexCoord.x;\n" + + " vTexCoord.y = 1.0 - aTexCoord.y;\n" + "}\n"; - // Note we flip the y-coordinate in the fragment shader from a - // coordinate system with (0,0) in the top left to one with (0,0) in - // the bottom left. + // We use highp because the screenshot textures + // we use are large and we stretch them alot + // so we need all the precision we can get. + // Unfortunately, highp is not required by ES 2.0 + // so on GPU's like Mali we end up getting mediump public static final String DEFAULT_FRAGMENT_SHADER = - "precision mediump float;\n" + + "precision highp float;\n" + "varying vec2 vTexCoord;\n" + "uniform sampler2D sTexture;\n" + "void main() {\n" + - " gl_FragColor = texture2D(sTexture, vec2(vTexCoord.x, 1.0 - vTexCoord.y));\n" + + " gl_FragColor = texture2D(sTexture, vTexCoord);\n" + "}\n"; public void setCheckerboardBitmap(Bitmap bitmap, float pageWidth, float pageHeight) { commit f789cd425e7808cca01058b0843ed0cda045bcb6 Author: Tomaž Vajngerl <tomaz.vajng...@collabora.com> Date: Wed Sep 24 20:38:13 2014 +0200 android: define mRootLayer directly as MultiTileLayer Change-Id: I6a1f6971482ea1de28977d7905ba9fd85921551d 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 e43a308..4f94e17 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 @@ -67,7 +67,7 @@ public class GeckoLayerClient implements LayerView.Listener { private boolean mRecordDrawTimes; private DrawTimingQueue mDrawTimingQueue; - private Layer mRootLayer; + private MultiTileLayer mRootLayer; /* The viewport that Gecko is currently displaying. */ private ViewportMetrics mGeckoViewport; @@ -129,9 +129,7 @@ public class GeckoLayerClient implements LayerView.Listener { } protected void updateLayerAfterDraw() { - if (mRootLayer instanceof MultiTileLayer) { - ((MultiTileLayer) mRootLayer).invalidate(); - } + mRootLayer.invalidate(); } public void beginDrawing(ViewportMetrics viewportMetrics) { @@ -250,7 +248,7 @@ public class GeckoLayerClient implements LayerView.Listener { synchronized (mLayerController) { // adjust the page dimensions to account for differences in zoom // between the rendered content (which is what the compositor tells us) - // and our zoom level (which may have diverged). + // and our zoom level (which may have diverged). float ourZoom = mLayerController.getZoomFactor(); pageWidth = pageWidth * ourZoom / zoom; pageHeight = pageHeight * ourZoom /zoom; @@ -273,16 +271,11 @@ public class GeckoLayerClient implements LayerView.Listener { } public List<SubTile> getTiles() { - if (mRootLayer instanceof MultiTileLayer) { - return ((MultiTileLayer) mRootLayer).getTiles(); - } - return null; + return mRootLayer.getTiles(); } public void addTile(SubTile tile) { - if (mRootLayer instanceof MultiTileLayer) { - ((MultiTileLayer) mRootLayer).addTile(tile); - } + mRootLayer.addTile(tile); } @Override diff --git a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/MultiTileLayer.java b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/MultiTileLayer.java index 469d7f5..e60e89d 100644 --- a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/MultiTileLayer.java +++ b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/MultiTileLayer.java @@ -42,7 +42,6 @@ import android.graphics.Point; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Region; -import android.util.Log; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; @@ -95,8 +94,6 @@ public class MultiTileLayer extends Layer { } private void validateTiles() { - Log.i(LOGTAG, "validateTiles()"); - // Set tile origins and resolution Point origin = new Point(); refreshTileMetrics(origin, getResolution(), false); @@ -120,8 +117,9 @@ public class MultiTileLayer extends Layer { RectF layerBounds = layer.getBounds(context); if (!RectF.intersects(layerBounds, context.viewport)) { - if (firstDirtyTile == null) + if (firstDirtyTile == null) { firstDirtyTile = layer; + } dirtyTiles++; } else { // This tile intersects with the screen and is dirty, @@ -196,8 +194,9 @@ public class MultiTileLayer extends Layer { for (SubTile layer : mTiles) { // Avoid work, only draw tiles that intersect with the viewport RectF layerBounds = layer.getBounds(context); - if (RectF.intersects(layerBounds, context.viewport)) + if (RectF.intersects(layerBounds, context.viewport)) { layer.draw(context); + } } } commit df433a70cd2fe564a4d046a0bbb1e90292978184 Author: Tomaž Vajngerl <tomaz.vajng...@collabora.com> Date: Wed Sep 24 15:19:58 2014 +0200 android: upgrade PanZoomController - add configurable zoom limits Change-Id: I19815f58af4d060cffe515829a2a5472d32bf83c diff --git a/android/experimental/LOAndroid3/src/java/org/libreoffice/LibreOfficeMainActivity.java b/android/experimental/LOAndroid3/src/java/org/libreoffice/LibreOfficeMainActivity.java index 35a320d..810ff26 100644 --- a/android/experimental/LOAndroid3/src/java/org/libreoffice/LibreOfficeMainActivity.java +++ b/android/experimental/LOAndroid3/src/java/org/libreoffice/LibreOfficeMainActivity.java @@ -118,6 +118,7 @@ public class LibreOfficeMainActivity extends Activity { } mLayerController = new LayerController(this); + mLayerController.setAllowZoom(true); mLayerClient = new GeckoLayerClient(this); mLayerController.setLayerClient(mLayerClient); mGeckoLayout.addView(mLayerController.getView(), 0); diff --git a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/DisplayPortCalculator.java b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/DisplayPortCalculator.java index a255974..88507e5 100644 --- a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/DisplayPortCalculator.java +++ b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/DisplayPortCalculator.java @@ -36,7 +36,7 @@ final class DisplayPortCalculator { private static final String PREF_DISPLAYPORT_VB_DANGER_Y_INCR = "gfx.displayport.strategy_vb.danger_y_incr"; private static final String PREF_DISPLAYPORT_PB_VELOCITY_THRESHOLD = "gfx.displayport.strategy_pb.threshold"; - private static DisplayPortStrategy sStrategy = new NoMarginStrategy(null); + private static DisplayPortStrategy sStrategy = new DynamicResolutionStrategy(null); static DisplayPortMetrics calculate(ImmutableViewportMetrics metrics, PointF velocity) { return sStrategy.calculate(metrics, (velocity == null ? ZERO_VELOCITY : velocity)); 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 ce047ee..e43a308 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 @@ -172,7 +172,7 @@ public class GeckoLayerClient implements LayerView.Listener { // Don't adjust page size when zooming unless zoom levels are // approximately equal. if (FloatUtils.fuzzyEquals(mLayerController.getZoomFactor(), mGeckoViewport.getZoomFactor())) { - mLayerController.setPageSize(mGeckoViewport.getPageSize()); + mLayerController.setPageSize(mGeckoViewport.getPageSize(), mGeckoViewport.getPageSize()); } } else { mLayerController.setViewportMetrics(mGeckoViewport); @@ -254,7 +254,7 @@ public class GeckoLayerClient implements LayerView.Listener { float ourZoom = mLayerController.getZoomFactor(); pageWidth = pageWidth * ourZoom / zoom; pageHeight = pageHeight * ourZoom /zoom; - mLayerController.setPageSize(new FloatSize(pageWidth, pageHeight)); + mLayerController.setPageSize(new FloatSize(pageWidth, pageHeight), new FloatSize(pageWidth, pageHeight)); // Here the page size of the document has changed, but the document being displayed // is still the same. Therefore, we don't need to send anything to browser.js; any // changes we need to make to the display port will get sent the next time we call @@ -296,13 +296,13 @@ public class GeckoLayerClient implements LayerView.Listener { } @Override - public void compositionResumeRequested() { + public void compositionResumeRequested(int width, int height) { } @Override public void surfaceChanged(int width, int height) { - compositionResumeRequested(); + compositionResumeRequested(width, height); renderRequested(); } 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 01559a1..277ed42 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 @@ -1,40 +1,7 @@ /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Android code. - * - * The Initial Developer of the Original Code is Mozilla Foundation. - * Portions created by the Initial Developer are Copyright (C) 2009-2010 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Patrick Walton <pcwal...@mozilla.com> - * Chris Lord <chrislord....@gmail.com> - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ + * 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; @@ -42,6 +9,7 @@ import android.content.Context; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BitmapFactory; +import android.graphics.Color; import android.graphics.PointF; import android.graphics.RectF; import android.view.GestureDetector; @@ -49,8 +17,6 @@ import android.view.GestureDetector; import org.mozilla.gecko.ui.PanZoomController; import org.mozilla.gecko.ui.SimpleScaleGestureDetector; -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 @@ -87,12 +53,15 @@ public class LayerController { private GeckoLayerClient mLayerClient; /* The layer client. */ /* The new color for the checkerboard. */ - private int mCheckerboardColor; + private int mCheckerboardColor = Color.WHITE; private boolean mCheckerboardShouldShowChecks; - private boolean mForceRedraw; + private boolean mAllowZoom; + private float mDefaultZoom; + private float mMinZoom; + private float mMaxZoom; - private static Pattern sColorPattern; + private boolean mForceRedraw; public LayerController(Context context) { mContext = context; @@ -124,6 +93,10 @@ public class LayerController { return mViewportMetrics.getViewport(); } + public RectF getCssViewport() { + return mViewportMetrics.getCssViewport(); + } + public FloatSize getViewportSize() { return mViewportMetrics.getSize(); } @@ -132,6 +105,10 @@ public class LayerController { return mViewportMetrics.getPageSize(); } + public FloatSize getCssPageSize() { + return mViewportMetrics.getCssPageSize(); + } + public PointF getOrigin() { return mViewportMetrics.getOrigin(); } @@ -189,12 +166,12 @@ public class LayerController { } /** Sets the current page size. You must hold the monitor while calling this. */ - public void setPageSize(FloatSize size) { - if (mViewportMetrics.getPageSize().fuzzyEquals(size)) + public void setPageSize(FloatSize size, FloatSize cssSize) { + if (mViewportMetrics.getCssPageSize().equals(cssSize)) return; ViewportMetrics viewportMetrics = new ViewportMetrics(mViewportMetrics); - viewportMetrics.setPageSize(size, size); + viewportMetrics.setPageSize(size, cssSize); mViewportMetrics = new ImmutableViewportMetrics(viewportMetrics); // Page size is owned by the layer client, so no need to notify it of @@ -294,8 +271,9 @@ public class LayerController { * correct. */ public PointF convertViewPointToLayerPoint(PointF viewPoint) { - if (mRootLayer == null) + if (mLayerClient == null) { return null; + } ImmutableViewportMetrics viewportMetrics = mViewportMetrics; PointF origin = viewportMetrics.getOrigin(); @@ -337,5 +315,41 @@ public class LayerController { mCheckerboardColor = newColor; mView.requestRender(); } -} + public void setAllowZoom(final boolean aValue) { + mAllowZoom = aValue; + mView.post(new Runnable() { + public void run() { + mView.getTouchEventHandler().setDoubleTapEnabled(aValue); + } + }); + } + + public boolean getAllowZoom() { + return mAllowZoom; + } + + public void setDefaultZoom(float aValue) { + mDefaultZoom = aValue; + } + + public float getDefaultZoom() { + return mDefaultZoom; + } + + public void setMinZoom(float aValue) { + mMinZoom = aValue; + } + + public float getMinZoom() { + return mMinZoom; + } + + public void setMaxZoom(float aValue) { + mMaxZoom = aValue; + } + + public float getMaxZoom() { + return mMaxZoom; + } +} diff --git a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/LayerView.java b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/LayerView.java index f981667..9c6a616 100644 --- a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/LayerView.java +++ b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/LayerView.java @@ -1,40 +1,7 @@ /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Android code. - * - * The Initial Developer of the Original Code is Mozilla Foundation. - * Portions created by the Initial Developer are Copyright (C) 2009-2010 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Patrick Walton <pcwal...@mozilla.com> - * Arkady Blyakher <rka...@mit.edu> - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ + * 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; @@ -42,7 +9,6 @@ package org.mozilla.gecko.gfx; import android.content.Context; import android.graphics.Bitmap; import android.graphics.PixelFormat; -import android.opengl.GLSurfaceView; import android.util.Log; import android.view.KeyEvent; import android.view.MotionEvent; @@ -227,7 +193,7 @@ public class LayerView extends SurfaceView implements SurfaceHolder.Callback { } - public GLSurfaceView.Renderer getRenderer() { + public LayerRenderer getRenderer() { return mRenderer; } @@ -235,7 +201,7 @@ public class LayerView extends SurfaceView implements SurfaceHolder.Callback { mListener = listener; } - public synchronized GLController getGLController() { + public GLController getGLController() { return mGLController; } @@ -288,7 +254,7 @@ public class LayerView extends SurfaceView implements SurfaceHolder.Callback { public interface Listener { void renderRequested(); void compositionPauseRequested(); - void compositionResumeRequested(); + void compositionResumeRequested(int width, int height); void surfaceChanged(int width, int height); } diff --git a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/TouchEventHandler.java b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/TouchEventHandler.java index d972ef3..78a141e 100644 --- a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/TouchEventHandler.java +++ b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/TouchEventHandler.java @@ -10,8 +10,8 @@ import android.os.SystemClock; import android.view.GestureDetector; import android.view.MotionEvent; import android.view.View.OnTouchListener; -import android.view.ViewConfiguration; +import org.mozilla.gecko.ui.PanZoomController; import org.mozilla.gecko.ui.SimpleScaleGestureDetector; import java.util.LinkedList; @@ -41,18 +41,24 @@ import java.util.Queue; * at some point after the first or second event in the block is processed in Gecko. * This code assumes we get EXACTLY ONE default-prevented notification for each block * of events. + * + * Note that even if all events are default-prevented, we still send specific types + * of notifications to the pan/zoom controller. The notifications are needed + * to respond to user actions a timely manner regardless of default-prevention, + * and fix issues like bug 749384. */ public final class TouchEventHandler { private static final String LOGTAG = "GeckoTouchEventHandler"; // The time limit for listeners to respond with preventDefault on touchevents // before we begin panning the page - private final int EVENT_LISTENER_TIMEOUT = ViewConfiguration.getLongPressTimeout(); + private final int EVENT_LISTENER_TIMEOUT = 200; private final LayerView mView; - private final LayerController mController; private final GestureDetector mGestureDetector; private final SimpleScaleGestureDetector mScaleGestureDetector; + private final PanZoomController mPanZoomController; + private final GestureDetector.OnDoubleTapListener mDoubleTapListener; // the queue of events that we are holding on to while waiting for a preventDefault // notification @@ -119,15 +125,16 @@ public final class TouchEventHandler { TouchEventHandler(Context context, LayerView view, LayerController controller) { mView = view; - mController = controller; mEventQueue = new LinkedList<MotionEvent>(); mGestureDetector = new GestureDetector(context, controller.getGestureListener()); mScaleGestureDetector = new SimpleScaleGestureDetector(controller.getScaleGestureListener()); + mPanZoomController = controller.getPanZoomController(); mListenerTimeoutProcessor = new ListenerTimeoutProcessor(); mDispatchEvents = true; - mGestureDetector.setOnDoubleTapListener(controller.getDoubleTapListener()); + mDoubleTapListener = controller.getDoubleTapListener(); + setDoubleTapEnabled(true); } /* This function MUST be called on the UI thread */ @@ -142,7 +149,18 @@ public final class TouchEventHandler { if (isDownEvent(event)) { // this is the start of a new block of events! whee! mHoldInQueue = mWaitForTouchListeners; + + // Set mDispatchEvents to true so that we are guaranteed to either queue these + // events or dispatch them. The only time we should not do either is once we've + // heard back from content to preventDefault this block. + mDispatchEvents = true; if (mHoldInQueue) { + // if the new block we are starting is the current block (i.e. there are no + // other blocks waiting in the queue, then we should let the pan/zoom controller + // know we are waiting for the touch listeners to run + if (mEventQueue.isEmpty()) { + mPanZoomController.waitingForTouchListeners(event); + } // if we're holding the events in the queue, set the timeout so that // we dispatch these events if we don't get a default-prevented notification mView.postDelayed(mListenerTimeoutProcessor, EVENT_LISTENER_TIMEOUT); @@ -164,6 +182,8 @@ public final class TouchEventHandler { mEventQueue.add(MotionEvent.obtain(event)); } else if (mDispatchEvents) { dispatchEvent(event); + } else if (touchFinished(event)) { + mPanZoomController.preventedTouchFinished(); } // notify gecko of the event @@ -193,6 +213,11 @@ public final class TouchEventHandler { } /* This function MUST be called on the UI thread. */ + public void setDoubleTapEnabled(boolean aValue) { + mGestureDetector.setOnDoubleTapListener(aValue ? mDoubleTapListener : null); + } + + /* This function MUST be called on the UI thread. */ public void setWaitForTouchListeners(boolean aValue) { mWaitForTouchListeners = aValue; } @@ -207,18 +232,32 @@ public final class TouchEventHandler { return (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_POINTER_DOWN); } + private boolean touchFinished(MotionEvent event) { + int action = (event.getAction() & MotionEvent.ACTION_MASK); + return (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL); + } + /** * Dispatch the event to the gesture detectors and the pan/zoom controller. */ private void dispatchEvent(MotionEvent event) { if (mGestureDetector.onTouchEvent(event)) { - return; + // An up/cancel event should get passed to both detectors, in + // case it comes from a pointer the scale detector is tracking. + switch (event.getAction() & MotionEvent.ACTION_MASK) { + case MotionEvent.ACTION_POINTER_UP: + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + break; + default: + return; + } } mScaleGestureDetector.onTouchEvent(event); if (mScaleGestureDetector.isInProgress()) { return; } - mController.getPanZoomController().onTouchEvent(event); + mPanZoomController.onTouchEvent(event); } /** @@ -244,6 +283,8 @@ public final class TouchEventHandler { // default-prevented. if (allowDefaultAction) { dispatchEvent(event); + } else if (touchFinished(event)) { + mPanZoomController.preventedTouchFinished(); } event = mEventQueue.peek(); if (event == null) { @@ -259,6 +300,7 @@ public final class TouchEventHandler { if (isDownEvent(event)) { // we have finished processing the block we were interested in. // now we wait for the next call to processEventBlock + mPanZoomController.waitingForTouchListeners(event); break; } // pop the event we peeked above, as it is still part of the block and 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 acded9c..8f81b5d 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 @@ -1,40 +1,7 @@ /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Android code. - * - * The Initial Developer of the Original Code is Mozilla Foundation. - * Portions created by the Initial Developer are Copyright (C) 2009-2012 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Patrick Walton <pcwal...@mozilla.com> - * Kartikaya Gupta <kgu...@mozilla.com> - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ + * 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.ui; @@ -52,6 +19,8 @@ import org.mozilla.gecko.gfx.LayerController; import org.mozilla.gecko.gfx.ViewportMetrics; import org.mozilla.gecko.util.FloatUtils; +import java.util.Arrays; +import java.util.StringTokenizer; import java.util.Timer; import java.util.TimerTask; @@ -85,7 +54,7 @@ public class PanZoomController 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 = { + private static float[] ZOOM_ANIMATION_FRAMES = new float[] { 0.00000f, /* 0 */ 0.10211f, /* 1 */ 0.19864f, /* 2 */ @@ -115,7 +84,11 @@ public class PanZoomController 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 */ - BOUNCE /* in a bounce animation */ + BOUNCE, /* in a bounce animation */ + + WAITING_LISTENERS, /* a state halfway between NOTHING and TOUCHING - the user has + put a finger down, but we don't yet know if a touch listener has + prevented the default actions yet. we still need to abort animations. */ } private final LayerController mController; @@ -156,6 +129,23 @@ public class PanZoomController } } + private void setZoomAnimationFrames(String frames) { + try { + if (frames.length() > 0) { + StringTokenizer st = new StringTokenizer(frames, ","); + float[] values = new float[st.countTokens()]; + for (int i = 0; i < values.length; i++) { + values[i] = Float.parseFloat(st.nextToken()); + } + ZOOM_ANIMATION_FRAMES = values; + } + } catch (NumberFormatException e) { + Log.e(LOGTAG, "Error setting zoom animation frames", e); + } finally { + Log.i(LOGTAG, "Zoom animation frames: " + Arrays.toString(ZOOM_ANIMATION_FRAMES)); + } + } + public boolean onTouchEvent(MotionEvent event) { switch (event.getAction() & MotionEvent.ACTION_MASK) { case MotionEvent.ACTION_DOWN: return onTouchStart(event); @@ -195,6 +185,30 @@ public class PanZoomController } } + /** This function must be called on the UI thread. */ + public void waitingForTouchListeners(MotionEvent event) { + checkMainThread(); + if ((event.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_DOWN) { + // this is the first touch point going down, so we enter the pending state + mSubscroller.cancel(); + // seting the state will kill any animations in progress, possibly leaving + // the page in overscroll + mState = PanZoomState.WAITING_LISTENERS; + } + } + + /** This function must be called on the UI thread. */ + public void preventedTouchFinished() { + checkMainThread(); + if (mState == PanZoomState.WAITING_LISTENERS) { + // if we enter here, we just finished a block of events whose default actions + // were prevented by touch listeners. Now there are no touch points left, so + // we need to reset our state and re-bounce because we might be in overscroll + mState = PanZoomState.NOTHING; + bounce(); + } + } + /** This must be called on the UI thread. */ public void pageSizeUpdated() { if (mState == PanZoomState.NOTHING) { @@ -222,10 +236,16 @@ public class PanZoomController switch (mState) { case ANIMATED_ZOOM: - return false; + // We just interrupted a double-tap animation, so force a redraw in + // case this touchstart is just a tap that doesn't end up triggering + // a redraw + mController.setForceRedraw(); + mController.notifyLayerClientOfGeometryChange(); + // fall through case FLING: case BOUNCE: case NOTHING: + case WAITING_LISTENERS: startTouch(event.getX(0), event.getY(0), event.getEventTime()); return false; case TOUCHING: @@ -244,11 +264,16 @@ public class PanZoomController private boolean onTouchMove(MotionEvent event) { switch (mState) { - case NOTHING: case FLING: case BOUNCE: + case WAITING_LISTENERS: // should never happen Log.e(LOGTAG, "Received impossible touch move while in " + mState); + // fall through + case ANIMATED_ZOOM: + case NOTHING: + // may happen if user double-taps and drags without lifting after the + // second tap. ignore the move if this happens. return false; case TOUCHING: @@ -274,7 +299,6 @@ public class PanZoomController track(event); return true; - case ANIMATED_ZOOM: case PINCHING: // scale gesture listener will handle this return false; @@ -286,12 +310,18 @@ public class PanZoomController private boolean onTouchEnd(MotionEvent event) { switch (mState) { - case NOTHING: case FLING: case BOUNCE: + case WAITING_LISTENERS: // should never happen Log.e(LOGTAG, "Received impossible touch end while in " + mState); + // fall through + case ANIMATED_ZOOM: + case NOTHING: + // may happen if user double-taps and drags without lifting after the + // second tap. ignore if this happens. return false; + case TOUCHING: mState = PanZoomState.NOTHING; // the switch into TOUCHING might have happened while the page was @@ -299,6 +329,7 @@ public class PanZoomController // was the case bounce(); return false; + case PANNING: case PANNING_LOCKED: case PANNING_HOLD: @@ -306,19 +337,28 @@ public class PanZoomController 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) { - mState = PanZoomState.NOTHING; + if (mState == PanZoomState.WAITING_LISTENERS) { + // we might get a cancel event from the TouchEventHandler while in the + // WAITING_LISTENERS state if the touch listeners prevent-default the + // block of events. at this point being in WAITING_LISTENERS is equivalent + // to being in NOTHING with the exception of possibly being in overscroll. + // so here we don't want to do anything right now; the overscroll will be + // corrected in preventedTouchFinished(). + return false; + } + cancelTouch(); + mState = PanZoomState.NOTHING; // ensure we snap back if we're overscrolled bounce(); return false; @@ -423,16 +463,17 @@ public class PanZoomController return; } - mState = PanZoomState.BOUNCE; - // set the animation target *after* setting state BOUNCE, so that - // the getRedrawHint() is returning false and we don't clobber the display - // port we set as a result of this animation target call. + // At this point we have already set mState to BOUNCE or ANIMATED_ZOOM, so + // getRedrawHint() is returning false. This means we can safely call + // setAnimationTarget to set the new final display port and not have it get + // clobbered by display ports from intermediate animation frames. mController.setAnimationTarget(metrics); startAnimationTimer(new BounceRunnable(bounceStartMetrics, metrics)); } /* Performs a bounce-back animation to the nearest valid viewport metrics. */ private void bounce() { + mState = PanZoomState.BOUNCE; bounce(getValidViewportMetrics()); } @@ -477,14 +518,14 @@ public class PanZoomController return getVelocity() < STOPPED_THRESHOLD; } - PointF getDisplacement() { + PointF resetDisplacement() { return new PointF(mX.resetDisplacement(), mY.resetDisplacement()); } private void updatePosition() { mX.displace(); mY.displace(); - PointF displacement = getDisplacement(); + PointF displacement = resetDisplacement(); if (FloatUtils.fuzzyEquals(displacement.x, 0.0f) && FloatUtils.fuzzyEquals(displacement.y, 0.0f)) { return; } @@ -542,13 +583,13 @@ public class PanZoomController * animation by setting the state to PanZoomState.NOTHING. Handle this case and bail * out. */ - if (mState != PanZoomState.BOUNCE) { + if (!(mState == PanZoomState.BOUNCE || mState == PanZoomState.ANIMATED_ZOOM)) { finishAnimation(); return; } /* Perform the next frame of the bounce-back animation. */ - if (mBounceFrame < EASE_OUT_ANIMATION_FRAMES.length) { + if (mBounceFrame < ZOOM_ANIMATION_FRAMES.length) { advanceBounce(); return; } @@ -562,7 +603,7 @@ public class PanZoomController /* Performs one frame of a bounce animation. */ private void advanceBounce() { synchronized (mController) { - float t = EASE_OUT_ANIMATION_FRAMES[mBounceFrame]; + float t = ZOOM_ANIMATION_FRAMES[mBounceFrame]; ViewportMetrics newMetrics = mBounceStartMetrics.interpolate(mBounceEndMetrics, t); mController.setViewportMetrics(newMetrics); mController.notifyLayerClientOfGeometryChange(); @@ -655,19 +696,37 @@ public class PanZoomController float focusX = viewport.width() / 2.0f; float focusY = viewport.height() / 2.0f; + float minZoomFactor = 0.0f; - if (viewport.width() > pageSize.width && pageSize.width > 0) { + float maxZoomFactor = MAX_ZOOM; + + if (mController.getMinZoom() > 0) + minZoomFactor = mController.getMinZoom(); + if (mController.getMaxZoom() > 0) + maxZoomFactor = mController.getMaxZoom(); + + if (!mController.getAllowZoom()) { + // If allowZoom is false, clamp to the default zoom level. + maxZoomFactor = minZoomFactor = mController.getDefaultZoom(); + } + + // Ensure minZoomFactor keeps the page at least as big as the viewport. + if (pageSize.width > 0) { float scaleFactor = viewport.width() / pageSize.width; minZoomFactor = Math.max(minZoomFactor, zoomFactor * scaleFactor); - focusX = 0.0f; + if (viewport.width() > pageSize.width) + focusX = 0.0f; } - if (viewport.height() > pageSize.height && pageSize.height > 0) { + if (pageSize.height > 0) { float scaleFactor = viewport.height() / pageSize.height; minZoomFactor = Math.max(minZoomFactor, zoomFactor * scaleFactor); - focusY = 0.0f; + if (viewport.height() > pageSize.height) + focusY = 0.0f; } - if (!FloatUtils.fuzzyEquals(minZoomFactor, 0.0f)) { + maxZoomFactor = Math.max(maxZoomFactor, minZoomFactor); + + if (zoomFactor < minZoomFactor) { // if one (or both) of the page dimensions is smaller than the viewport, // zoom using the top/left as the focus on that axis. this prevents the // scenario where, if both dimensions are smaller than the viewport, but @@ -675,9 +734,9 @@ public class PanZoomController // after applying the scale PointF center = new PointF(focusX, focusY); viewportMetrics.scaleTo(minZoomFactor, center); - } else if (zoomFactor > MAX_ZOOM) { + } else if (zoomFactor > maxZoomFactor) { PointF center = new PointF(viewport.width() / 2.0f, viewport.height() / 2.0f); - viewportMetrics.scaleTo(MAX_ZOOM, center); + viewportMetrics.scaleTo(maxZoomFactor, center); } /* Now we pan to the right origin. */ @@ -717,9 +776,11 @@ public class PanZoomController if (mState == PanZoomState.ANIMATED_ZOOM) return false; + if (!mController.getAllowZoom()) + return false; + mState = PanZoomState.PINCHING; mLastZoomFocus = new PointF(detector.getFocusX(), detector.getFocusY()); - cancelTouch(); return true; @@ -729,7 +790,8 @@ public class PanZoomController public boolean onScale(SimpleScaleGestureDetector detector) { Log.d(LOGTAG, "onScale in state " + mState); - if (mState == PanZoomState.ANIMATED_ZOOM) + + if (mState != PanZoomState.PINCHING) return false; float prevSpan = detector.getPreviousSpan(); @@ -752,13 +814,31 @@ public class PanZoomController synchronized (mController) { float newZoomFactor = mController.getZoomFactor() * spanRatio; - if (newZoomFactor >= MAX_ZOOM) { - // apply resistance when zooming past MAX_ZOOM, - // such that it asymptotically reaches MAX_ZOOM + 1.0 + float minZoomFactor = 0.0f; + float maxZoomFactor = MAX_ZOOM; + + if (mController.getMinZoom() > 0) + minZoomFactor = mController.getMinZoom(); + if (mController.getMaxZoom() > 0) + maxZoomFactor = mController.getMaxZoom(); + + if (newZoomFactor < minZoomFactor) { + // apply resistance when zooming past minZoomFactor, + // such that it asymptotically reaches minZoomFactor / 2.0 // but never exceeds that - float excessZoom = newZoomFactor - MAX_ZOOM; + final float rate = 0.5f; // controls how quickly we approach the limit + float excessZoom = minZoomFactor - newZoomFactor; + excessZoom = 1.0f - (float)Math.exp(-excessZoom * rate); + newZoomFactor = minZoomFactor * (1.0f - excessZoom / 2.0f); + } + + if (newZoomFactor > maxZoomFactor) { + // apply resistance when zooming past maxZoomFactor, + // such that it asymptotically reaches maxZoomFactor + 1.0 + // but never exceeds that + float excessZoom = newZoomFactor - maxZoomFactor; excessZoom = 1.0f - (float)Math.exp(-excessZoom); - newZoomFactor = MAX_ZOOM + excessZoom; + newZoomFactor = maxZoomFactor + excessZoom; } mController.scrollBy(new PointF(mLastZoomFocus.x - detector.getFocusX(), @@ -807,23 +887,37 @@ public class PanZoomController } @Override - public boolean onDown(MotionEvent motionEvent) { - return false; + public boolean onSingleTapUp(MotionEvent motionEvent) { + // When zooming is enabled, wait to see if there's a double-tap. + if (mController.getAllowZoom()) + return false; + return true; } @Override public boolean onSingleTapConfirmed(MotionEvent motionEvent) { + // When zooming is disabled, we handle this in onSingleTapUp. + if (!mController.getAllowZoom()) + return false; return true; } @Override public boolean onDoubleTap(MotionEvent motionEvent) { + if (!mController.getAllowZoom()) + return false; return true; } - public void cancelTouch() { + private void cancelTouch() { } + /** + * Zoom to a specified rect IN CSS PIXELS. + * + * While we usually use device pixels, @zoomToRect must be specified in CSS + * pixels. + */ private boolean animatedZoomTo(RectF zoomToRect) { mState = PanZoomState.ANIMATED_ZOOM; final float startZoom = mController.getZoomFactor(); @@ -849,10 +943,11 @@ public class PanZoomController zoomToRect.right = zoomToRect.left + newWidth; } - float finalZoom = viewport.width() * startZoom / zoomToRect.width(); + float finalZoom = viewport.width() / zoomToRect.width(); ViewportMetrics finalMetrics = new ViewportMetrics(mController.getViewportMetrics()); - finalMetrics.setOrigin(new PointF(zoomToRect.left, zoomToRect.top)); + finalMetrics.setOrigin(new PointF(zoomToRect.left * finalMetrics.getZoomFactor(), + zoomToRect.top * finalMetrics.getZoomFactor())); finalMetrics.scaleTo(finalZoom, new PointF(0.0f, 0.0f)); // 2. now run getValidViewportMetrics on it, so that the target viewport is commit 0455b3d4b874db06a205d1133f48bcd323665911 Author: Tomaž Vajngerl <tomaz.vajng...@collabora.com> Date: Wed Sep 24 13:18:03 2014 +0200 android: update LayerRenderer to newer Fennec code Change-Id: Idc7c49351c17dc9953d55ee7f1b42e497074c85a diff --git a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java index 506c57e..bdcfb1d 100644 --- a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java +++ b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java @@ -152,7 +152,7 @@ public class LOKitThread extends Thread { Log.i(LOGTAG, "Done generate thumbnail!"); if (bitmap != null) { Log.i(LOGTAG, "Setting checkboard image!"); - mApplication.getLayerController().getView().changeCheckerboardBitmap(bitmap); + mApplication.getLayerController().getView().changeCheckerboardBitmap(bitmap, mTileProvider.getPageWidth(), mTileProvider.getPageHeight()); Log.i(LOGTAG, "Done setting checkboard image!!"); mCheckboardImageSet = true; } 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 ade6a46..5146b22 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 @@ -1,41 +1,7 @@ /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Android code. - * - * The Initial Developer of the Original Code is Mozilla Foundation. - * Portions created by the Initial Developer are Copyright (C) 2009-2010 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Patrick Walton <pcwal...@mozilla.com> - * Chris Lord <chrislord....@gmail.com> - * Arkady Blyakher <rka...@mit.edu> - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ + * 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; @@ -88,7 +54,8 @@ public class LayerRenderer implements GLSurfaceView.Renderer { private final ScrollbarLayer mHorizScrollLayer; private final ScrollbarLayer mVertScrollLayer; private final FadeRunnable mFadeRunnable; - private final FloatBuffer mCoordBuffer; + private ByteBuffer mCoordByteBuffer; + private FloatBuffer mCoordBuffer; private RenderContext mLastPageContext; private int mMaxTextureSize; private int mBackgroundColor; @@ -153,20 +120,26 @@ public class LayerRenderer implements GLSurfaceView.Renderer { " gl_FragColor = texture2D(sTexture, vec2(vTexCoord.x, 1.0 - vTexCoord.y));\n" + "}\n"; - public void setCheckerboardBitmap(Bitmap bitmap) { + public void setCheckerboardBitmap(Bitmap bitmap, float pageWidth, float pageHeight) { mCheckerboardLayer.setBitmap(bitmap); mCheckerboardLayer.beginTransaction(); try { + mCheckerboardLayer.setPosition(new Rect(0, 0, Math.round(pageWidth), + Math.round(pageHeight))); mCheckerboardLayer.invalidate(); } finally { mCheckerboardLayer.endTransaction(); } } - public void updateCheckerboardBitmap(Bitmap bitmap, float x, float y, float width, float height) { + public void updateCheckerboardBitmap(Bitmap bitmap, float x, float y, + float width, float height, + float pageWidth, float pageHeight) { mCheckerboardLayer.updateBitmap(bitmap, x, y, width, height); mCheckerboardLayer.beginTransaction(); try { + mCheckerboardLayer.setPosition(new Rect(0, 0, Math.round(pageWidth), + Math.round(pageHeight))); mCheckerboardLayer.invalidate(); } finally { mCheckerboardLayer.endTransaction(); @@ -175,12 +148,6 @@ public class LayerRenderer implements GLSurfaceView.Renderer { public void resetCheckerboard() { mCheckerboardLayer.reset(); - mCheckerboardLayer.beginTransaction(); - try { - mCheckerboardLayer.invalidate(); - } finally { - mCheckerboardLayer.endTransaction(); - } } public LayerRenderer(LayerView view) { @@ -205,9 +172,22 @@ public class LayerRenderer implements GLSurfaceView.Renderer { // Initialize the FloatBuffer that will be used to store all vertices and texture // coordinates in draw() commands. - ByteBuffer byteBuffer = DirectBufferAllocator.allocate(COORD_BUFFER_SIZE * 4); - byteBuffer.order(ByteOrder.nativeOrder()); - mCoordBuffer = byteBuffer.asFloatBuffer(); + mCoordByteBuffer = DirectBufferAllocator.allocate(COORD_BUFFER_SIZE * 4); + mCoordByteBuffer.order(ByteOrder.nativeOrder()); + mCoordBuffer = mCoordByteBuffer.asFloatBuffer(); + } + + @Override + protected void finalize() throws Throwable { + try { + if (mCoordByteBuffer != null) { + DirectBufferAllocator.free(mCoordByteBuffer); + mCoordByteBuffer = null; + mCoordBuffer = null; + } + } finally { + super.finalize(); + } } public void onSurfaceCreated(GL10 gl, EGLConfig config) { @@ -408,20 +388,6 @@ public class LayerRenderer implements GLSurfaceView.Renderer { }).start(); } - private void updateCheckerboardImage() { - int checkerboardColor = mView.getController().getCheckerboardColor(); - boolean showChecks = mView.getController().checkerboardShouldShowChecks(); - - mCheckerboardLayer.beginTransaction(); // called on compositor thread - try { - if (mCheckerboardLayer.updateBackground(showChecks, checkerboardColor)) - mCheckerboardLayer.invalidate(); - } finally { - mCheckerboardLayer.endTransaction(); - } - - } - /* * create a vertex shader type (GLES20.GL_VERTEX_SHADER) * or a fragment shader type (GLES20.GL_FRAGMENT_SHADER) diff --git a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/LayerView.java b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/LayerView.java index 62a26bf..f981667 100644 --- a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/LayerView.java +++ b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/LayerView.java @@ -348,8 +348,8 @@ public class LayerView extends SurfaceView implements SurfaceHolder.Callback { } } - public void changeCheckerboardBitmap(Bitmap bitmap) { + public void changeCheckerboardBitmap(Bitmap bitmap, float pageWidth, float pageHeight) { mRenderer.resetCheckerboard(); - mRenderer.setCheckerboardBitmap(bitmap); + mRenderer.setCheckerboardBitmap(bitmap, pageWidth, pageHeight); } } commit 2550cb0124f19269521cecfba39afa4ac4023431 Author: Tomaž Vajngerl <tomaz.vajng...@collabora.com> Date: Wed Sep 24 12:49:08 2014 +0200 android: introduce TileProviderFactory Change-Id: I98ba16b4d1537ddeb2f8a29d15a803d527ccafe3 diff --git a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java index d11c4c9..506c57e 100644 --- a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java +++ b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java @@ -26,6 +26,7 @@ public class LOKitThread extends Thread { private boolean mCheckboardImageSet = false; public LOKitThread() { + TileProviderFactory.initialize(); } private RectF normlizeRect(ImmutableViewportMetrics metrics) { @@ -35,9 +36,9 @@ public class LOKitThread extends Thread { } private Rect roundToTileSize(RectF input, int tileSize) { - int minX = (Math.round(input.left) / tileSize) * tileSize; - int minY = (Math.round(input.top) / tileSize) * tileSize; - int maxX = ((Math.round(input.right) / tileSize) + 1) * tileSize; + int minX = (Math.round(input.left) / tileSize) * tileSize; + int minY = (Math.round(input.top) / tileSize) * tileSize; + int maxX = ((Math.round(input.right) / tileSize) + 1) * tileSize; int maxY = ((Math.round(input.bottom) / tileSize) + 1) * tileSize; return new Rect(minX, minY, maxX, maxY); } @@ -136,7 +137,7 @@ public class LOKitThread extends Thread { if (mTileProvider != null) { mTileProvider.close(); } - mTileProvider = new LOKitTileProvider(mApplication.getLayerController(), filename); + mTileProvider = TileProviderFactory.create(mApplication.getLayerController(), filename); boolean isReady = mTileProvider.isReady(); if (isReady) { updateCheckbardImage(); @@ -191,4 +192,8 @@ public class LOKitThread extends Thread { Log.i(LOGTAG, "Event: " + event.getTypeString()); mEventQueue.add(event); } + + public void clearQueue() { + mEventQueue.clear(); + } } diff --git a/android/experimental/LOAndroid3/src/java/org/libreoffice/LibreOfficeMainActivity.java b/android/experimental/LOAndroid3/src/java/org/libreoffice/LibreOfficeMainActivity.java index 284afe9..35a320d 100644 --- a/android/experimental/LOAndroid3/src/java/org/libreoffice/LibreOfficeMainActivity.java +++ b/android/experimental/LOAndroid3/src/java/org/libreoffice/LibreOfficeMainActivity.java @@ -13,7 +13,6 @@ import android.widget.AdapterView; import android.widget.ListView; import android.widget.RelativeLayout; -import org.libreoffice.kit.LibreOfficeKit; import org.mozilla.gecko.gfx.GeckoLayerClient; import org.mozilla.gecko.gfx.LayerController; @@ -111,19 +110,18 @@ public class LibreOfficeMainActivity extends Activity { mDrawerList.setOnItemClickListener(new DocumentPartClickListener()); } - LibreOfficeKit.loadStatic(); + if (sLOKitThread == null) { + sLOKitThread = new LOKitThread(); + sLOKitThread.start(); + } else { + sLOKitThread.clearQueue(); + } mLayerController = new LayerController(this); mLayerClient = new GeckoLayerClient(this); mLayerController.setLayerClient(mLayerClient); mGeckoLayout.addView(mLayerController.getView(), 0); - if (sLOKitThread == null) { - sLOKitThread = new LOKitThread(); - sLOKitThread.start(); - } - - sLOKitThread.mEventQueue.clear(); LOKitShell.sendEvent(LOEvent.load(inputFile)); Log.w(LOGTAG, "UI almost up"); diff --git a/android/experimental/LOAndroid3/src/java/org/libreoffice/TileProviderFactory.java b/android/experimental/LOAndroid3/src/java/org/libreoffice/TileProviderFactory.java new file mode 100644 index 0000000..5cd3ed0 --- /dev/null +++ b/android/experimental/LOAndroid3/src/java/org/libreoffice/TileProviderFactory.java @@ -0,0 +1,31 @@ +package org.libreoffice; + + +import org.libreoffice.kit.LibreOfficeKit; +import org.mozilla.gecko.gfx.LayerController; + +public class TileProviderFactory { + private static TileProviderID currentTileProvider = TileProviderID.LOKIT; + + private TileProviderFactory() { + + } + + public static void initialize() { + if (currentTileProvider == TileProviderID.LOKIT) { + LibreOfficeKit.loadStatic(); + } + } + + public static TileProvider create(LayerController layerController, String filename) { + if (currentTileProvider == TileProviderID.LOKIT) { + return new LOKitTileProvider(layerController, filename); + } else { + return new MockTileProvider(layerController, filename); + } + } + + private static enum TileProviderID { + MOCK, LOKIT + } +} \ No newline at end of file commit ef2b36f8d3a023d5562e742fe4db57dafdd28115 Author: Tomaž Vajngerl <tomaz.vajng...@collabora.com> Date: Wed Sep 24 10:12:20 2014 +0200 android: GeckoLayerClient update Change-Id: Ie684a4d3ef012b004ede52265750da5497db434e diff --git a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOEvent.java b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOEvent.java index 4aa32cb..f16b2da 100644 --- a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOEvent.java +++ b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOEvent.java @@ -20,7 +20,7 @@ public class LOEvent { private int mPartIndex; private String mFilename; - public LOEvent(int type, int widthPixels, int heightPixels, int tileWidth, int tileHeight) { + public LOEvent(int type, int widthPixels, int heightPixels) { mType = type; mTypeString = "Size Changed: " + widthPixels + " " + heightPixels; } @@ -56,8 +56,8 @@ public class LOEvent { return new LOEvent(DRAW, rect); } - public static LOEvent sizeChanged(int widthPixels, int heightPixels, int tileWidth, int tileHeight) { - return new LOEvent(SIZE_CHANGED, widthPixels, heightPixels, tileWidth, tileHeight); + public static LOEvent sizeChanged(int widthPixels, int heightPixels) { + return new LOEvent(SIZE_CHANGED, widthPixels, heightPixels); } public static LOEvent tileSize(IntSize tileSize) { diff --git a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/DrawTimingQueue.java b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/DrawTimingQueue.java new file mode 100644 index 0000000..ce868f1 --- /dev/null +++ b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/DrawTimingQueue.java @@ -0,0 +1,95 @@ +/* -*- 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.os.SystemClock; + +/** + * A custom-built data structure to assist with measuring draw times. + * + * This class maintains a fixed-size circular buffer of DisplayPortMetrics + * objects and associated timestamps. It provides only three operations, which + * is all we require for our purposes of measuring draw times. Note + * in particular that the class is designed so that even though it is + * accessed from multiple threads, it does not require synchronization; + * any concurrency errors that result from this are handled gracefully. + * + * Assuming an unrolled buffer so that mTail is greater than mHead, the data + * stored in the buffer at entries [mHead, mTail) will never be modified, and + * so are "safe" to read. If this reading is done on the same thread that + * owns mHead, then reading the range [mHead, mTail) is guaranteed to be safe + * since the range itself will not shrink. + */ +final class DrawTimingQueue { + private static final String LOGTAG = "GeckoDrawTimingQueue"; + private static final int BUFFER_SIZE = 16; + + private final DisplayPortMetrics[] mMetrics; + private final long[] mTimestamps; + + private int mHead; + private int mTail; + + DrawTimingQueue() { + mMetrics = new DisplayPortMetrics[BUFFER_SIZE]; + mTimestamps = new long[BUFFER_SIZE]; + mHead = BUFFER_SIZE - 1; + mTail = 0; + } + + /** + * Add a new entry to the tail of the queue. If the buffer is full, + * do nothing. This must only be called from the Java UI thread. + */ + boolean add(DisplayPortMetrics metrics) { + if (mHead == mTail) { + return false; + } + mMetrics[mTail] = metrics; + mTimestamps[mTail] = SystemClock.uptimeMillis(); + mTail = (mTail + 1) % BUFFER_SIZE; + return true; + } + + /** + * Find the timestamp associated with the given metrics, AND remove + * all metrics objects from the start of the queue up to and including + * the one provided. Note that because of draw coalescing, the metrics + * object passed in here may not be the one at the head of the queue, + * and so we must iterate our way through the list to find it. + * This must only be called from the compositor thread. + */ + long findTimeFor(DisplayPortMetrics metrics) { + // keep a copy of the tail pointer so that we ignore new items + // added to the queue while we are searching. this is fine because + // the one we are looking for will either have been added already + // or will not be in the queue at all. + int tail = mTail; + // walk through the "safe" range from mHead to tail; these entries + // will not be modified unless we change mHead. + int i = (mHead + 1) % BUFFER_SIZE; + while (i != tail) { + if (mMetrics[i].fuzzyEquals(metrics)) { + // found it, copy out the timestamp to a local var BEFORE + // changing mHead or add could clobber the timestamp. + long timestamp = mTimestamps[i]; + mHead = i; + return timestamp; + } + i = (i + 1) % BUFFER_SIZE; + } + return -1; + } + + /** + * Reset the buffer to empty. + * This must only be called from the compositor thread. + */ + void reset() { + // we can only modify mHead on this thread. + mHead = (mTail + BUFFER_SIZE - 1) % BUFFER_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 2d735a8..ce047ee 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 @@ -44,6 +44,7 @@ import android.graphics.RectF; import android.os.SystemClock; import android.util.DisplayMetrics; import android.util.Log; +import android.view.View; import org.libreoffice.LOEvent; import org.libreoffice.LOKitShell; @@ -51,64 +52,71 @@ import org.libreoffice.LibreOfficeMainActivity; import org.mozilla.gecko.util.FloatUtils; import java.util.List; +import java.util.regex.Pattern; -public class GeckoLayerClient { +public class GeckoLayerClient implements LayerView.Listener { private static final String LOGTAG = "GeckoLayerClient"; - private static final long MIN_VIEWPORT_CHANGE_DELAY = 25L; - private static final IntSize TILE_SIZE = new IntSize(256, 256); + private LayerController mLayerController; + private LayerRenderer mLayerRenderer; + private boolean mLayerRendererInitialized; - protected IntSize mScreenSize; + private IntSize mScreenSize; + private IntSize mWindowSize; private DisplayPortMetrics mDisplayPort; - protected Layer mTileLayer; + private boolean mRecordDrawTimes; + private DrawTimingQueue mDrawTimingQueue; + + private Layer mRootLayer; + /* The viewport that Gecko is currently displaying. */ - protected ViewportMetrics mGeckoViewport; + private ViewportMetrics mGeckoViewport; + /* The viewport that Gecko will display when drawing is finished */ - protected ViewportMetrics mNewGeckoViewport; - protected LayerController mLayerController; + private ViewportMetrics mNewGeckoViewport; private Context mContext; + private static final long MIN_VIEWPORT_CHANGE_DELAY = 25L; private long mLastViewportChangeTime; private boolean mPendingViewportAdjust; private boolean mViewportSizeChanged; + private boolean mIgnorePaintsPendingViewportSizeChange; + private boolean mFirstPaint = true; + // mUpdateViewportOnEndDraw is used to indicate that we received a // viewport update notification while drawing. therefore, when the // draw finishes, we need to update the entire viewport rather than // just the page size. this boolean should always be accessed from // inside a transaction, so no synchronization is needed. private boolean mUpdateViewportOnEndDraw; + private String mLastCheckerboardColor; + private static Pattern sColorPattern; + + /* Used as a temporary ViewTransform by syncViewportInfo */ + private ViewTransform mCurrentViewTransform; + public GeckoLayerClient(Context context) { mContext = context; mScreenSize = new IntSize(0, 0); + mWindowSize = new IntSize(0, 0); mDisplayPort = new DisplayPortMetrics(); + mRecordDrawTimes = true; + mDrawTimingQueue = new DrawTimingQueue(); + mCurrentViewTransform = new ViewTransform(0, 0, 1); } - protected void setupLayer() { - if (mTileLayer == null) { - Log.i(LOGTAG, "Creating MultiTileLayer"); - mTileLayer = new MultiTileLayer(TILE_SIZE); - mLayerController.setRoot(mTileLayer); - } - } + /** Attaches the root layer to the layer controller so that Gecko appears. */ + public void setLayerController(LayerController layerController) { + LayerView view = layerController.getView(); - protected void updateLayerAfterDraw() { - if (mTileLayer instanceof MultiTileLayer) { - ((MultiTileLayer) mTileLayer).invalidate(); - } - } + mLayerController = layerController; - protected IntSize getTileSize() { - return TILE_SIZE; - } + mRootLayer = new MultiTileLayer(new IntSize(256, 256)); - /** - * Attaches the root layer to the layer controller so that Gecko appears. - */ - public void setLayerController(LayerController layerController) { - mLayerController = layerController; + view.setListener(this); + layerController.setRoot(mRootLayer); - layerController.setRoot(mTileLayer); if (mGeckoViewport != null) { layerController.setViewportMetrics(mGeckoViewport); } @@ -116,10 +124,19 @@ public class GeckoLayerClient { sendResizeEventIfNecessary(true); } + DisplayPortMetrics getDisplayPort() { + return mDisplayPort; + } + + protected void updateLayerAfterDraw() { + if (mRootLayer instanceof MultiTileLayer) { + ((MultiTileLayer) mRootLayer).invalidate(); + } + } + public void beginDrawing(ViewportMetrics viewportMetrics) { - setupLayer(); mNewGeckoViewport = viewportMetrics; - mTileLayer.beginTransaction(); + mRootLayer.beginTransaction(); } public void endDrawing() { @@ -129,16 +146,12 @@ public class GeckoLayerClient { mUpdateViewportOnEndDraw = false; updateLayerAfterDraw(); } finally { - mTileLayer.endTransaction(); + mRootLayer.endTransaction(); } } Log.i(LOGTAG, "zerdatime " + SystemClock.uptimeMillis() + " - endDrawing"); } - DisplayPortMetrics 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 @@ -150,8 +163,8 @@ public class GeckoLayerClient { PointF displayportOrigin = mGeckoViewport.getOrigin(); RectF position = mGeckoViewport.getViewport(); - mTileLayer.setPosition(RectUtils.round(position)); - mTileLayer.setResolution(mGeckoViewport.getZoomFactor()); + mRootLayer.setPosition(RectUtils.round(position)); + mRootLayer.setResolution(mGeckoViewport.getZoomFactor()); Log.e(LOGTAG, "### updateViewport onlyUpdatePageSize=" + onlyUpdatePageSize + " getTileViewport " + mGeckoViewport); @@ -173,51 +186,39 @@ public class GeckoLayerClient { DisplayMetrics metrics = new DisplayMetrics(); LibreOfficeMainActivity.mAppContext.getWindowManager().getDefaultDisplay().getMetrics(metrics); + View view = mLayerController.getView(); IntSize newScreenSize = new IntSize(metrics.widthPixels, metrics.heightPixels); + IntSize newWindowSize = new IntSize(view.getWidth(), view.getHeight()); // Return immediately if the screen size hasn't changed or the viewport // size is zero (which indicates that the rendering surface hasn't been // allocated yet). boolean screenSizeChanged = !mScreenSize.equals(newScreenSize); + boolean windowSizeChanged = !mWindowSize.equals(newWindowSize); - if (!force && !screenSizeChanged) { + if (!force && !screenSizeChanged && !windowSizeChanged) { return; } mScreenSize = newScreenSize; + mWindowSize = newWindowSize; if (screenSizeChanged) { Log.d(LOGTAG, "Screen-size changed to " + mScreenSize); } - IntSize tileSize = getTileSize(); - LOEvent event = LOEvent.sizeChanged(metrics.widthPixels, metrics.heightPixels, tileSize.width, tileSize.height); - LOKitShell.sendEvent(event); - } - - public void render() { - adjustViewportWithThrottling(); - } - - private void adjustViewportWithThrottling() { - if (!mLayerController.getRedrawHint()) - return; - - if (mPendingViewportAdjust) - return; - - long timeDelta = System.currentTimeMillis() - mLastViewportChangeTime; - if (timeDelta < MIN_VIEWPORT_CHANGE_DELAY) { - mLayerController.getView().postDelayed(new AdjustRunnable(), MIN_VIEWPORT_CHANGE_DELAY - timeDelta); - mPendingViewportAdjust = true; - } else { - adjustViewport(null); + if (windowSizeChanged) { + Log.d(LOGTAG, "Window-size changed to " + mWindowSize); } + + LOEvent event = LOEvent.sizeChanged(metrics.widthPixels, metrics.heightPixels); + LOKitShell.sendEvent(event); } public void viewportSizeChanged() { - mViewportSizeChanged = true; + sendResizeEventIfNecessary(true); + LOKitShell.viewSizeChanged(); } void adjustViewport(DisplayPortMetrics displayPort) { @@ -234,13 +235,31 @@ public class GeckoLayerClient { mDisplayPort = displayPort; mGeckoViewport = clampedMetrics; + if (mRecordDrawTimes) { + mDrawTimingQueue.add(displayPort); + } + LOKitShell.sendEvent(LOEvent.viewport(clampedMetrics)); if (mViewportSizeChanged) { mViewportSizeChanged = false; LOKitShell.viewSizeChanged(); } + } - mLastViewportChangeTime = System.currentTimeMillis(); + public void setPageSize(float zoom, float pageWidth, float pageHeight, float cssPageWidth, float cssPageHeight) { + synchronized (mLayerController) { + // adjust the page dimensions to account for differences in zoom + // between the rendered content (which is what the compositor tells us) + // and our zoom level (which may have diverged). + float ourZoom = mLayerController.getZoomFactor(); + pageWidth = pageWidth * ourZoom / zoom; + pageHeight = pageHeight * ourZoom /zoom; + mLayerController.setPageSize(new FloatSize(pageWidth, pageHeight)); + // Here the page size of the document has changed, but the document being displayed + // is still the same. Therefore, we don't need to send anything to browser.js; any + // changes we need to make to the display port will get sent the next time we call + // adjustViewport(). + } } public void geometryChanged() { @@ -250,24 +269,41 @@ public class GeckoLayerClient { } public ViewportMetrics getGeckoViewportMetrics() { - // Return a copy, as we modify this inside the Gecko thread - if (mGeckoViewport != null) - return new ViewportMetrics(mGeckoViewport); - return null; + return mGeckoViewport; } + public List<SubTile> getTiles() { + if (mRootLayer instanceof MultiTileLayer) { + return ((MultiTileLayer) mRootLayer).getTiles(); + } + return null; + } public void addTile(SubTile tile) { - if (mTileLayer instanceof MultiTileLayer) { - ((MultiTileLayer) mTileLayer).addTile(tile); + if (mRootLayer instanceof MultiTileLayer) { + ((MultiTileLayer) mRootLayer).addTile(tile); } } - public List<SubTile> getTiles() { - if (mTileLayer instanceof MultiTileLayer) { - return ((MultiTileLayer) mTileLayer).getTiles(); - } - return null; + @Override + public void renderRequested() { + + } + + @Override + public void compositionPauseRequested() { + + } + + @Override + public void compositionResumeRequested() { + + } + + @Override + public void surfaceChanged(int width, int height) { + compositionResumeRequested(); + renderRequested(); } private class AdjustRunnable implements Runnable { commit 1fa58e5c60ac136e92e91c4efa0cdcc5c28afa75 Author: Tomaž Vajngerl <tomaz.vajng...@collabora.com> Date: Wed Sep 24 09:10:12 2014 +0200 android: update DisplayPortCalculator Change-Id: Ib47822940e83e8fb78a0a8b1e18028646bfb35ae diff --git a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/DisplayPortCalculator.java b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/DisplayPortCalculator.java index a0cb229..a255974 100644 --- a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/DisplayPortCalculator.java +++ b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/DisplayPortCalculator.java @@ -7,16 +7,36 @@ package org.mozilla.gecko.gfx; import android.graphics.PointF; import android.graphics.RectF; +import android.util.FloatMath; import android.util.Log; +import org.json.JSONArray; import org.libreoffice.LOKitShell; import org.mozilla.gecko.util.FloatUtils; +import java.util.Map; + final class DisplayPortCalculator { private static final String LOGTAG = "GeckoDisplayPortCalculator"; private static final PointF ZERO_VELOCITY = new PointF(0, 0); - private static DisplayPortStrategy sStrategy = new FixedMarginStrategy(); + // Keep this in sync with the TILEDLAYERBUFFER_TILE_SIZE defined in gfx/layers/TiledLayerBuffer.h + private static final int TILE_SIZE = 256; + + private static final String PREF_DISPLAYPORT_STRATEGY = "gfx.displayport.strategy"; + private static final String PREF_DISPLAYPORT_FM_MULTIPLIER = "gfx.displayport.strategy_fm.multiplier"; + private static final String PREF_DISPLAYPORT_FM_DANGER_X = "gfx.displayport.strategy_fm.danger_x"; + private static final String PREF_DISPLAYPORT_FM_DANGER_Y = "gfx.displayport.strategy_fm.danger_y"; + private static final String PREF_DISPLAYPORT_VB_MULTIPLIER = "gfx.displayport.strategy_vb.multiplier"; + private static final String PREF_DISPLAYPORT_VB_VELOCITY_THRESHOLD = "gfx.displayport.strategy_vb.threshold"; + private static final String PREF_DISPLAYPORT_VB_REVERSE_BUFFER = "gfx.displayport.strategy_vb.reverse_buffer"; + private static final String PREF_DISPLAYPORT_VB_DANGER_X_BASE = "gfx.displayport.strategy_vb.danger_x_base"; + private static final String PREF_DISPLAYPORT_VB_DANGER_Y_BASE = "gfx.displayport.strategy_vb.danger_y_base"; + private static final String PREF_DISPLAYPORT_VB_DANGER_X_INCR = "gfx.displayport.strategy_vb.danger_x_incr"; + private static final String PREF_DISPLAYPORT_VB_DANGER_Y_INCR = "gfx.displayport.strategy_vb.danger_y_incr"; + private static final String PREF_DISPLAYPORT_PB_VELOCITY_THRESHOLD = "gfx.displayport.strategy_pb.threshold"; + + private static DisplayPortStrategy sStrategy = new NoMarginStrategy(null); static DisplayPortMetrics calculate(ImmutableViewportMetrics metrics, PointF velocity) { return sStrategy.calculate(metrics, (velocity == null ? ZERO_VELOCITY : velocity)); @@ -29,35 +49,78 @@ final class DisplayPortCalculator { return sStrategy.aboutToCheckerboard(metrics, (velocity == null ? ZERO_VELOCITY : velocity), displayPort); } + static boolean drawTimeUpdate(long millis, int pixels) { + return sStrategy.drawTimeUpdate(millis, pixels); + } + + static void resetPageState() { + sStrategy.resetPageState(); + } + + static void addPrefNames(JSONArray prefs) { + prefs.put(PREF_DISPLAYPORT_STRATEGY); + prefs.put(PREF_DISPLAYPORT_FM_MULTIPLIER); + prefs.put(PREF_DISPLAYPORT_FM_DANGER_X); + prefs.put(PREF_DISPLAYPORT_FM_DANGER_Y); + prefs.put(PREF_DISPLAYPORT_VB_MULTIPLIER); + prefs.put(PREF_DISPLAYPORT_VB_VELOCITY_THRESHOLD); + prefs.put(PREF_DISPLAYPORT_VB_REVERSE_BUFFER); + prefs.put(PREF_DISPLAYPORT_VB_DANGER_X_BASE); + prefs.put(PREF_DISPLAYPORT_VB_DANGER_Y_BASE); + prefs.put(PREF_DISPLAYPORT_VB_DANGER_X_INCR); + prefs.put(PREF_DISPLAYPORT_VB_DANGER_Y_INCR); + prefs.put(PREF_DISPLAYPORT_PB_VELOCITY_THRESHOLD); + } + /** * Set the active strategy to use. * See the gfx.displayport.strategy pref in mobile/android/app/mobile.js to see the * mapping between ints and strategies. */ - static void setStrategy(int strategy) { + static boolean setStrategy(Map<String, Integer> prefs) { + Integer strategy = prefs.get(PREF_DISPLAYPORT_STRATEGY); + if (strategy == null) { + return false; + } + switch (strategy) { case 0: - default: - sStrategy = new FixedMarginStrategy(); + sStrategy = new FixedMarginStrategy(prefs); break; case 1: - sStrategy = new VelocityBiasStrategy(); + sStrategy = new VelocityBiasStrategy(prefs); break; case 2: - sStrategy = new DynamicResolutionStrategy(); + sStrategy = new DynamicResolutionStrategy(prefs); break; case 3: - sStrategy = new NoMarginStrategy(); + sStrategy = new NoMarginStrategy(prefs); + break; + case 4: + sStrategy = new PredictionBiasStrategy(prefs); break; + default: + Log.e(LOGTAG, "Invalid strategy index specified"); + return false; } - Log.i(LOGTAG, "Set strategy " + sStrategy.getClass().getName()); + Log.i(LOGTAG, "Set strategy " + sStrategy.toString()); + return true; + } + + private static float getFloatPref(Map<String, Integer> prefs, String prefName, int defaultValue) { + Integer value = (prefs == null ? null : prefs.get(prefName)); + return (float)(value == null || value < 0 ? defaultValue : value) / 1000f; } - private interface DisplayPortStrategy { + private static abstract class DisplayPortStrategy { /** Calculates a displayport given a viewport and panning velocity. */ - public DisplayPortMetrics calculate(ImmutableViewportMetrics metrics, PointF velocity); + public abstract DisplayPortMetrics calculate(ImmutableViewportMetrics metrics, PointF velocity); /** Returns true if a checkerboard is about to be visible and we should not throttle drawing. */ - public boolean aboutToCheckerboard(ImmutableViewportMetrics metrics, PointF velocity, DisplayPortMetrics displayPort); + public abstract boolean aboutToCheckerboard(ImmutableViewportMetrics metrics, PointF velocity, DisplayPortMetrics displayPort); + /** Notify the strategy of a new recorded draw time. Return false to turn off draw time recording. */ + public boolean drawTimeUpdate(long millis, int pixels) { return false; } + /** Reset any page-specific state stored, as the page being displayed has changed. */ + public void resetPageState() {} } /** @@ -100,6 +163,24 @@ final class DisplayPortCalculator { } /** + * Expand the given margins such that when they are applied on the viewport, the resulting rect + * does not have any partial tiles, except when it is clipped by the page bounds. This assumes + * the tiles are TILE_SIZE by TILE_SIZE and start at the origin, such that there will always be + * a tile at (0,0)-(TILE_SIZE,TILE_SIZE)). + */ + private static DisplayPortMetrics getTileAlignedDisplayPortMetrics(RectF margins, float zoom, ImmutableViewportMetrics metrics) { + float left = metrics.viewportRectLeft - margins.left; + float top = metrics.viewportRectTop - margins.top; + float right = metrics.viewportRectRight + margins.right; + float bottom = metrics.viewportRectBottom + margins.bottom; + left = Math.max(0.0f, TILE_SIZE * FloatMath.floor(left / TILE_SIZE)); + top = Math.max(0.0f, TILE_SIZE * FloatMath.floor(top / TILE_SIZE)); + right = Math.min(metrics.pageSizeWidth, TILE_SIZE * FloatMath.ceil(right / TILE_SIZE)); + bottom = Math.min(metrics.pageSizeHeight, TILE_SIZE * FloatMath.ceil(bottom / TILE_SIZE)); + return new DisplayPortMetrics(left, top, right, bottom, zoom); + } + + /** * Adjust the given margins so if they are applied on the viewport in the metrics, the resulting rect * does not exceed the page bounds. This code will maintain the total margin amount for a given axis; * it assumes that margins.left + metrics.getWidth() + margins.right is less than or equal to @@ -133,9 +214,24 @@ final class DisplayPortCalculator { } /** + * Clamp the given rect to the page bounds and return it. + */ + private static RectF clampToPageBounds(RectF rect, ImmutableViewportMetrics metrics) { + rect.left = Math.max(rect.left, 0); + rect.top = Math.max(rect.top, 0); + rect.right = Math.min(rect.right, metrics.pageSizeWidth); + rect.bottom = Math.min(rect.bottom, metrics.pageSizeHeight); + return rect; + } + + /** * This class implements the variation where we basically don't bother with a a display port. */ - private static class NoMarginStrategy implements DisplayPortStrategy { + private static class NoMarginStrategy extends DisplayPortStrategy { + NoMarginStrategy(Map<String, Integer> prefs) { + // no prefs in this strategy + } + public DisplayPortMetrics calculate(ImmutableViewportMetrics metrics, PointF velocity) { return new DisplayPortMetrics(metrics.viewportRectLeft, metrics.viewportRectTop, @@ -147,6 +243,11 @@ final class DisplayPortCalculator { public boolean aboutToCheckerboard(ImmutableViewportMetrics metrics, PointF velocity, DisplayPortMetrics displayPort) { return true; } + + @Override + public String toString() { + return "NoMarginStrategy"; + } } /** @@ -157,15 +258,21 @@ final class DisplayPortCalculator { * and/or (b) increasing the buffer on the other axis to compensate for the reduced buffer on * one axis. */ - private static class FixedMarginStrategy implements DisplayPortStrategy { + private static class FixedMarginStrategy extends DisplayPortStrategy { // The length of each axis of the display port will be the corresponding view length // multiplied by this factor. - private static final float SIZE_MULTIPLIER = 1.5f; + private final float SIZE_MULTIPLIER; // If the visible rect is within the danger zone (measured as a fraction of the view size // from the edge of the displayport) we start redrawing to minimize checkerboarding. - private static final float DANGER_ZONE_X_MULTIPLIER = 0.10f; - private static final float DANGER_ZONE_Y_MULTIPLIER = 0.20f; + private final float DANGER_ZONE_X_MULTIPLIER; + private final float DANGER_ZONE_Y_MULTIPLIER; + + FixedMarginStrategy(Map<String, Integer> prefs) { + SIZE_MULTIPLIER = getFloatPref(prefs, PREF_DISPLAYPORT_FM_MULTIPLIER, 2000); + DANGER_ZONE_X_MULTIPLIER = getFloatPref(prefs, PREF_DISPLAYPORT_FM_DANGER_X, 100); + DANGER_ZONE_Y_MULTIPLIER = getFloatPref(prefs, PREF_DISPLAYPORT_FM_DANGER_Y, 200); + } public DisplayPortMetrics calculate(ImmutableViewportMetrics metrics, PointF velocity) { float displayPortWidth = metrics.getWidth() * SIZE_MULTIPLIER; @@ -191,15 +298,7 @@ final class DisplayPortCalculator { margins.bottom = verticalBuffer - margins.top; margins = shiftMarginsForPageBounds(margins, metrics); - // 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 DisplayPortMetrics(metrics.viewportRectLeft - margins.left, - metrics.viewportRectTop - margins.top, - metrics.viewportRectRight + margins.right, - metrics.viewportRectBottom + margins.bottom, - metrics.zoomFactor); + return getTileAlignedDisplayPortMetrics(margins, metrics.zoomFactor, metrics); } public boolean aboutToCheckerboard(ImmutableViewportMetrics metrics, PointF velocity, DisplayPortMetrics displayPort) { @@ -209,6 +308,11 @@ final class DisplayPortCalculator { RectF adjustedViewport = expandByDangerZone(metrics.getViewport(), DANGER_ZONE_X_MULTIPLIER, DANGER_ZONE_Y_MULTIPLIER, metrics); return !displayPort.contains(adjustedViewport); } + + @Override + public String toString() { + return "FixedMarginStrategy mult=" + SIZE_MULTIPLIER + ", dangerX=" + DANGER_ZONE_X_MULTIPLIER + ", dangerY=" + DANGER_ZONE_Y_MULTIPLIER; + } } /** @@ -217,14 +321,67 @@ final class DisplayPortCalculator { * they are affected by the panning velocity. Specifically, if we are panning on one axis, * we remove the margins on the other axis because we are likely axis-locked. Also once * we are panning in one direction above a certain threshold velocity, we shift the buffer - * so that it is entirely in the direction of the pan. + * so that it is almost entirely in the direction of the pan, with a little bit in the + * reverse direction. */ - private static class VelocityBiasStrategy implements DisplayPortStrategy { + private static class VelocityBiasStrategy extends DisplayPortStrategy { // The length of each axis of the display port will be the corresponding view length // multiplied by this factor. - private static final float SIZE_MULTIPLIER = 1.2f; + private final float SIZE_MULTIPLIER; // The velocity above which we apply the velocity bias - private static final float VELOCITY_THRESHOLD = LOKitShell.getDpi() / 32f; + private final float VELOCITY_THRESHOLD; + // How much of the buffer to keep in the reverse direction of the velocity + private final float REVERSE_BUFFER; + // If the visible rect is within the danger zone we start redrawing to minimize + // checkerboarding. the danger zone amount is a linear function of the form: + // viewportsize * (base + velocity * incr) + // where base and incr are configurable values. + private final float DANGER_ZONE_BASE_X_MULTIPLIER; + private final float DANGER_ZONE_BASE_Y_MULTIPLIER; + private final float DANGER_ZONE_INCR_X_MULTIPLIER; + private final float DANGER_ZONE_INCR_Y_MULTIPLIER; + + VelocityBiasStrategy(Map<String, Integer> prefs) { + SIZE_MULTIPLIER = getFloatPref(prefs, PREF_DISPLAYPORT_VB_MULTIPLIER, 2000); + VELOCITY_THRESHOLD = LOKitShell.getDpi() * getFloatPref(prefs, PREF_DISPLAYPORT_VB_VELOCITY_THRESHOLD, 32); + REVERSE_BUFFER = getFloatPref(prefs, PREF_DISPLAYPORT_VB_REVERSE_BUFFER, 200); + DANGER_ZONE_BASE_X_MULTIPLIER = getFloatPref(prefs, PREF_DISPLAYPORT_VB_DANGER_X_BASE, 1000); + DANGER_ZONE_BASE_Y_MULTIPLIER = getFloatPref(prefs, PREF_DISPLAYPORT_VB_DANGER_Y_BASE, 1000); + DANGER_ZONE_INCR_X_MULTIPLIER = getFloatPref(prefs, PREF_DISPLAYPORT_VB_DANGER_X_INCR, 0); + DANGER_ZONE_INCR_Y_MULTIPLIER = getFloatPref(prefs, PREF_DISPLAYPORT_VB_DANGER_Y_INCR, 0); + } + + /** + * Split the given amounts into margins based on the VELOCITY_THRESHOLD and REVERSE_BUFFER values. + * If the velocity is above the VELOCITY_THRESHOLD on an axis, split the amount into REVERSE_BUFFER + * and 1.0 - REVERSE_BUFFER fractions. The REVERSE_BUFFER fraction is set as the margin in the + * direction opposite to the velocity, and the remaining fraction is set as the margin in the direction + * of the velocity. If the velocity is lower than VELOCITY_THRESHOLD, split the amount evenly into the + * two margins on that axis. + */ + private RectF velocityBiasedMargins(float xAmount, float yAmount, PointF velocity) { + RectF margins = new RectF(); + + if (velocity.x > VELOCITY_THRESHOLD) { + margins.left = xAmount * REVERSE_BUFFER; + } else if (velocity.x < -VELOCITY_THRESHOLD) { + margins.left = xAmount * (1.0f - REVERSE_BUFFER); + } else { + margins.left = xAmount / 2.0f; + } + margins.right = xAmount - margins.left; + + if (velocity.y > VELOCITY_THRESHOLD) { + margins.top = yAmount * REVERSE_BUFFER; + } else if (velocity.y < -VELOCITY_THRESHOLD) { + margins.top = yAmount * (1.0f - REVERSE_BUFFER); + } else { + margins.top = yAmount / 2.0f; + } + margins.bottom = yAmount - margins.top; + + return margins; + } public DisplayPortMetrics calculate(ImmutableViewportMetrics metrics, PointF velocity) { float displayPortWidth = metrics.getWidth() * SIZE_MULTIPLIER; @@ -245,43 +402,43 @@ final class DisplayPortCalculator { float horizontalBuffer = displayPortWidth - metrics.getWidth(); float verticalBuffer = displayPortHeight - metrics.getHeight(); - // if we're panning above the VELOCITY_THRESHOLD on an axis, apply the margin so that it - // is entirely in the direction of panning. Otherwise, split the margin evenly on both sides of - // the display port. - RectF margins = new RectF(); - if (velocity.x > VELOCITY_THRESHOLD) { - margins.right = horizontalBuffer; - } else if (velocity.x < -VELOCITY_THRESHOLD) { - margins.left = horizontalBuffer; - } else { - margins.left = horizontalBuffer / 2.0f; - margins.right = horizontalBuffer - margins.left; - } - if (velocity.y > VELOCITY_THRESHOLD) { - margins.bottom = verticalBuffer; - } else if (velocity.y < -VELOCITY_THRESHOLD) { - margins.top = verticalBuffer; - } else { - margins.top = verticalBuffer / 2.0f; - margins.bottom = verticalBuffer - margins.top; - } - // and finally shift the margins to account for page bounds + // split the buffer amounts into margins based on velocity, and shift it to + // take into account the page bounds + RectF margins = velocityBiasedMargins(horizontalBuffer, verticalBuffer, velocity); margins = shiftMarginsForPageBounds(margins, metrics); - return new DisplayPortMetrics(metrics.viewportRectLeft - margins.left, - metrics.viewportRectTop - margins.top, - metrics.viewportRectRight + margins.right, - metrics.viewportRectBottom + margins.bottom, - metrics.zoomFactor); + return getTileAlignedDisplayPortMetrics(margins, metrics.zoomFactor, metrics); } public boolean aboutToCheckerboard(ImmutableViewportMetrics metrics, PointF velocity, DisplayPortMetrics displayPort) { - // Since we have such a small margin, we want to be drawing more aggressively. At the start of a - // pan the velocity is going to be large so we're almost certainly going to go into checkerboard - // on every frame, so drawing all the time seems like the right thing. At the end of the pan we - // want to re-center the displayport and draw stuff on all sides, so again we don't want to throttle - // there. When we're not panning we're not drawing anyway so it doesn't make a difference there. - return true; + // calculate the danger zone amounts based on the prefs + float dangerZoneX = metrics.getWidth() * (DANGER_ZONE_BASE_X_MULTIPLIER + (velocity.x * DANGER_ZONE_INCR_X_MULTIPLIER)); ... etc. - the rest is truncated
_______________________________________________ Libreoffice-commits mailing list libreoffice-comm...@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/libreoffice-commits