android/experimental/LOAndroid3/src/java/org/libreoffice/LOEvent.java | 10 android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java | 35 - android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitTileProvider.java | 6 android/experimental/LOAndroid3/src/java/org/libreoffice/LibreOfficeMainActivity.java | 3 android/experimental/LOAndroid3/src/java/org/mozilla/gecko/ZoomConstraints.java | 46 ++ android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/GeckoLayerClient.java | 20 android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/ImmutableViewportMetrics.java | 170 ++++++- android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/LayerController.java | 118 +---- android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/LayerRenderer.java | 56 +- android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/MultiTileLayer.java | 54 -- android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/PointUtils.java | 16 android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/RectUtils.java | 35 - android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/ScreenshotLayer.java | 60 ++ android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/ViewportMetrics.java | 95 ---- android/experimental/LOAndroid3/src/java/org/mozilla/gecko/ui/Axis.java | 92 ++-- android/experimental/LOAndroid3/src/java/org/mozilla/gecko/ui/PanZoomController.java | 214 ++++------ android/experimental/LOAndroid3/src/java/org/mozilla/gecko/ui/PanZoomTarget.java | 24 + android/experimental/LOAndroid3/src/java/org/mozilla/gecko/ui/SubdocumentScrollHelper.java | 16 18 files changed, 589 insertions(+), 481 deletions(-)
New commits: commit 27953d41222ba0410769b381525c59f4395301e3 Author: Tomaž Vajngerl <tomaz.vajng...@collabora.com> Date: Sun Sep 28 22:30:38 2014 +0200 android: minimize the rounding error, clean-up MultiTileLayer Change-Id: Ib167acf5914596b69ee240255aaab173a0570038 diff --git a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java index f808f57..ba7102e 100644 --- a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java +++ b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java @@ -28,10 +28,7 @@ public class LOKitThread extends Thread { } private boolean draw() throws InterruptedException { - int pageWidth = mTileProvider.getPageWidth(); - int pageHeight = mTileProvider.getPageHeight(); - - RectF rect = new RectF(0, 0, pageWidth, pageHeight); + RectF rect = new RectF(0, 0, mTileProvider.getPageWidth(), mTileProvider.getPageHeight()); DisplayMetrics displayMetrics = LibreOfficeMainActivity.mAppContext.getResources().getDisplayMetrics(); mViewportMetrics = new ImmutableViewportMetrics(displayMetrics); mViewportMetrics = mViewportMetrics.setPageRect(rect, rect); @@ -72,6 +69,8 @@ public class LOKitThread extends Thread { boolean isReady = mTileProvider.isReady(); if (isReady) { updateCheckbardImage(); + RectF rect = new RectF(0, 0, mTileProvider.getPageWidth(), mTileProvider.getPageHeight()); + mController.setPageRect(rect, rect); mController.setForceRedraw(); } return isReady; @@ -79,13 +78,9 @@ public class LOKitThread extends Thread { private void updateCheckbardImage() { 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, mTileProvider.getPageWidth(), mTileProvider.getPageHeight()); - Log.i(LOGTAG, "Done setting checkboard image!!"); mCheckboardImageSet = true; } } diff --git a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitTileProvider.java b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitTileProvider.java index 5b1b718..77a833e 100644 --- a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitTileProvider.java +++ b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitTileProvider.java @@ -112,7 +112,11 @@ public class LOKitTileProvider implements TileProvider { Bitmap bitmap = Bitmap.createBitmap(TILE_SIZE, TILE_SIZE, Bitmap.Config.ARGB_8888); if (mDocument != null) { - mDocument.paintTile(buffer, TILE_SIZE, TILE_SIZE, Math.round(pixelToTwip(x, mDPI)/zoom), Math.round(pixelToTwip(y, mDPI)/ zoom), Math.round(mTileWidth / zoom), Math.round(mTileHeight/zoom)); + float twipX = pixelToTwip(x, mDPI) / zoom; + float twipY = pixelToTwip(y, mDPI) / zoom; + float twipWidth = mTileWidth / zoom; + float twipHeight = mTileHeight / zoom; + mDocument.paintTile(buffer, TILE_SIZE, TILE_SIZE, (int) twipX, (int) twipY, (int)twipWidth, (int)twipHeight); } else { Log.e(LOGTAG, "Document is null!!"); } 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 c4a445d..14a08e2 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 org.libreoffice.TileProvider; import org.mozilla.gecko.util.FloatUtils; @@ -51,23 +50,16 @@ import java.util.ArrayList; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; -/** - * Encapsulates the logic needed to draw a layer made of multiple tiles. - */ public class MultiTileLayer extends Layer { private static final String LOGTAG = "MultiTileLayer"; private static int TILE_SIZE = 256; - private final List<SubTile> mTiles; + private final List<SubTile> mTiles = new CopyOnWriteArrayList<SubTile>(); private TileProvider tileProvider; - private float currentZoomFactor; private RectF tileViewPort = new RectF(); - private boolean shouldRefreshZoom = true; - private RectF currentPageRect = new RectF(); public MultiTileLayer() { super(); - mTiles = new CopyOnWriteArrayList<SubTile>(); } public void invalidate() { @@ -131,10 +123,10 @@ public class MultiTileLayer extends Layer { if (origin != null) { Rect position = layer.getPosition(); - int positionX = origin.x + Math.round(layer.x / layer.zoom); - int positionY = origin.y + Math.round(layer.y / layer.zoom); - int tileSize = Math.round(256.0f / layer.zoom); - position.set(positionX, positionY, positionX + tileSize, positionY + tileSize); + float positionX = origin.x + (layer.x / layer.zoom); + float positionY = origin.y + (layer.y / layer.zoom); + float tileSize = TILE_SIZE / layer.zoom; + position.set((int) positionX, (int) positionY, (int) (positionX + tileSize + 1), (int) (positionY + tileSize + 1)); layer.setPosition(position); } if (resolution >= 0.0f) { @@ -170,10 +162,6 @@ public class MultiTileLayer extends Layer { super.endTransaction(); } - private RectF normlizeRect(RectF rect, FloatSize pageSize) { - return new RectF(rect.left / pageSize.width, rect.top / pageSize.height, rect.right / pageSize.width, rect.bottom / pageSize.height); - } - private RectF roundToTileSize(RectF input, int tileSize) { float minX = (Math.round(input.left) / tileSize) * tileSize; float minY = (Math.round(input.top) / tileSize) * tileSize; @@ -198,12 +186,6 @@ public class MultiTileLayer extends Layer { @Override public void draw(RenderContext context) { - if (tileProvider == null) { - return; - } - - currentPageRect = context.pageRect; - for (SubTile layer : mTiles) { // Avoid work, only draw tiles that intersect with the viewport RectF layerBounds = layer.getBounds(context); @@ -229,19 +211,13 @@ public class MultiTileLayer extends Layer { } public void reevaluateTiles(ImmutableViewportMetrics viewportMetrics) { - if (currentZoomFactor != viewportMetrics.zoomFactor) { - currentZoomFactor = viewportMetrics.zoomFactor; - } - RectF newTileViewPort = inflate(roundToTileSize(viewportMetrics.getViewport(), TILE_SIZE), TILE_SIZE); - Log.i(LOGTAG, "reevaluateTiles ( " + viewportMetrics + " )"); - if (tileViewPort != newTileViewPort) { tileViewPort = newTileViewPort; cleanTiles(); - addNewTiles(); - markTiles(); + addNewTiles(viewportMetrics); + markTiles(viewportMetrics); } } @@ -256,24 +232,24 @@ public class MultiTileLayer extends Layer { mTiles.removeAll(tilesToRemove); } - private void addNewTiles() { + private void addNewTiles(ImmutableViewportMetrics viewportMetrics) { for (float y = tileViewPort.top; y < tileViewPort.bottom; y += TILE_SIZE) { - if (y > currentPageRect.height()) { + if (y > viewportMetrics.getPageHeight()) { continue; } for (float x = tileViewPort.left; x < tileViewPort.right; x += TILE_SIZE) { - if (x > currentPageRect.width()) { + if (x > viewportMetrics.getPageWidth()) { continue; } boolean contains = false; for (SubTile tile : mTiles) { - if (tile.x == x && tile.y == y) { + if (tile.x == x && tile.y == y && tile.zoom == viewportMetrics.zoomFactor) { contains = true; } } if (!contains) { - CairoImage image = tileProvider.createTile(x, y, currentZoomFactor); - SubTile tile = new SubTile(image, (int)x, (int)y, currentZoomFactor); + CairoImage image = tileProvider.createTile(x, y, viewportMetrics.zoomFactor); + SubTile tile = new SubTile(image, (int)x, (int)y, viewportMetrics.zoomFactor); tile.beginTransaction(); mTiles.add(tile); } @@ -281,9 +257,9 @@ public class MultiTileLayer extends Layer { } } - private void markTiles() { + private void markTiles(ImmutableViewportMetrics viewportMetrics) { for (SubTile tile : mTiles) { - if (FloatUtils.fuzzyEquals(tile.zoom, currentZoomFactor)) { + if (FloatUtils.fuzzyEquals(tile.zoom, viewportMetrics.zoomFactor)) { RectF tileRect = new RectF(tile.x, tile.y, tile.x + TILE_SIZE, tile.y + TILE_SIZE); if (!RectF.intersects(tileViewPort, tileRect)) { tile.markForRemoval(); commit 295f3c12dfc76b6faa438074bc77f8ac8ecadf06 Author: Tomaž Vajngerl <tomaz.vajng...@collabora.com> Date: Sun Sep 28 21:44:56 2014 +0200 android: LayerRendered - rename member to mScreenShotLayer Change-Id: I16fbda06c75bbf80e7d2c2b045418297589c6ff7 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 85ef846..dcedaae 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 @@ -48,7 +48,7 @@ public class LayerRenderer implements GLSurfaceView.Renderer { private final LayerView mView; private final SingleTileLayer mBackgroundLayer; - private final ScreenshotLayer mCheckerboardLayer; + private final ScreenshotLayer mScreenshotLayer; private final NinePatchTileLayer mShadowLayer; private TextLayer mFrameRateLayer; private final ScrollbarLayer mHorizScrollLayer; @@ -82,9 +82,6 @@ public class LayerRenderer implements GLSurfaceView.Renderer { private int mSampleHandle; private int mTMatrixHandle; - private int mSurfaceWidth; - private int mSurfaceHeight; - // column-major matrix applied to each vertex to shift the viewport from // one ranging from (-1, -1),(1,1) to (0,0),(1,1) and to scale all sizes by // a factor of 2 to fill up the screen @@ -129,33 +126,33 @@ public class LayerRenderer implements GLSurfaceView.Renderer { "}\n"; public void setCheckerboardBitmap(Bitmap bitmap, float pageWidth, float pageHeight) { - mCheckerboardLayer.setBitmap(bitmap); - mCheckerboardLayer.beginTransaction(); + mScreenshotLayer.setBitmap(bitmap); + mScreenshotLayer.beginTransaction(); try { - mCheckerboardLayer.setPosition(new Rect(0, 0, Math.round(pageWidth), + mScreenshotLayer.setPosition(new Rect(0, 0, Math.round(pageWidth), Math.round(pageHeight))); - mCheckerboardLayer.invalidate(); + mScreenshotLayer.invalidate(); } finally { - mCheckerboardLayer.endTransaction(); + mScreenshotLayer.endTransaction(); } } 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(); + mScreenshotLayer.updateBitmap(bitmap, x, y, width, height); + mScreenshotLayer.beginTransaction(); try { - mCheckerboardLayer.setPosition(new Rect(0, 0, Math.round(pageWidth), + mScreenshotLayer.setPosition(new Rect(0, 0, Math.round(pageWidth), Math.round(pageHeight))); - mCheckerboardLayer.invalidate(); + mScreenshotLayer.invalidate(); } finally { - mCheckerboardLayer.endTransaction(); + mScreenshotLayer.endTransaction(); } } public void resetCheckerboard() { - mCheckerboardLayer.reset(); + mScreenshotLayer.reset(); } public LayerRenderer(LayerView view) { @@ -166,7 +163,7 @@ public class LayerRenderer implements GLSurfaceView.Renderer { CairoImage backgroundImage = new BufferedCairoImage(controller.getBackgroundPattern()); mBackgroundLayer = new SingleTileLayer(true, backgroundImage); - mCheckerboardLayer = ScreenshotLayer.create(); + mScreenshotLayer = ScreenshotLayer.create(); CairoImage shadowImage = new BufferedCairoImage(controller.getShadowPattern()); mShadowLayer = new NinePatchTileLayer(shadowImage); @@ -196,6 +193,20 @@ public class LayerRenderer implements GLSurfaceView.Renderer { } } + public void destroy() { + DirectBufferAllocator.free(mCoordByteBuffer); + mCoordByteBuffer = null; + mCoordBuffer = null; + mScreenshotLayer.destroy(); + mBackgroundLayer.destroy(); + mShadowLayer.destroy(); + mHorizScrollLayer.destroy(); + mVertScrollLayer.destroy(); + if (mFrameRateLayer != null) { + mFrameRateLayer.destroy(); + } + } + public void onSurfaceCreated(GL10 gl, EGLConfig config) { checkMonitoringEnabled(); createDefaultProgram(); @@ -322,9 +333,6 @@ public class LayerRenderer implements GLSurfaceView.Renderer { } public void onSurfaceChanged(GL10 gl, final int width, final int height) { - mSurfaceWidth = width; - mSurfaceHeight = height; - GLES20.glViewport(0, 0, width, height); if (mFrameRateLayer != null) { @@ -510,7 +518,7 @@ public class LayerRenderer implements GLSurfaceView.Renderer { if (rootLayer != null) mUpdated &= rootLayer.update(mPageContext); // called on compositor thread mUpdated &= mBackgroundLayer.update(mScreenContext); // called on compositor thread mUpdated &= mShadowLayer.update(mPageContext); // called on compositor thread - mUpdated &= mCheckerboardLayer.update(mPageContext); // called on compositor thread + mUpdated &= mScreenshotLayer.update(mPageContext); // called on compositor thread if (mFrameRateLayer != null) mUpdated &= mFrameRateLayer.update(mScreenContext); // called on compositor thread mUpdated &= mVertScrollLayer.update(mPageContext); // called on compositor thread mUpdated &= mHorizScrollLayer.update(mPageContext); // called on compositor thread @@ -591,13 +599,13 @@ public class LayerRenderer implements GLSurfaceView.Renderer { if (mView.getController().checkerboardShouldShowChecks()) { /* Find the area the root layer will render into, to mask the checkerboard layer */ Rect rootMask = getMaskForLayer(mView.getController().getRoot()); - mCheckerboardLayer.setMask(rootMask); + mScreenshotLayer.setMask(rootMask); /* Scissor around the page-rect, in case the page has shrunk * since the screenshot layer was last updated. */ setScissorRect(); // Calls glEnable(GL_SCISSOR_TEST)) - mCheckerboardLayer.draw(mPageContext); + mScreenshotLayer.draw(mPageContext); } } @@ -644,7 +652,7 @@ public class LayerRenderer implements GLSurfaceView.Renderer { /* restrict the viewport to page bounds so we don't * count overscroll as checkerboard */ - if (!viewport.intersect(0, 0, mPageRect.width(), mPageRect.height())) { + if (!viewport.intersect(mPageRect)) { /* if the rectangles don't intersect intersect() doesn't change viewport so we set it to empty by hand */ commit d935e7be2c8818d028ab5ce718e7c9c5126cbf32 Author: Tomaž Vajngerl <tomaz.vajng...@collabora.com> Date: Sun Sep 28 11:58:58 2014 +0200 android: revert ScreenshotLayer draw method back Own draw method expands the picture correctly but for some reason the SingleTileLayer draw method does not so revert back. Change-Id: Ie8e39fc46b84ae410439a781928ff79bc1503d10 diff --git a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/ScreenshotLayer.java b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/ScreenshotLayer.java index 5f82213..c52b39b 100644 --- a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/ScreenshotLayer.java +++ b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/ScreenshotLayer.java @@ -8,10 +8,13 @@ package org.mozilla.gecko.gfx; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Paint; +import android.graphics.Rect; +import android.opengl.GLES20; import org.libreoffice.kit.DirectBufferAllocator; import java.nio.ByteBuffer; +import java.nio.FloatBuffer; public class ScreenshotLayer extends SingleTileLayer { private static final int SCREENSHOT_SIZE_LIMIT = 1048576; @@ -75,8 +78,61 @@ public class ScreenshotLayer extends SingleTileLayer { @Override public void draw(RenderContext context) { - if (mHasImage) - super.draw(context); + // mTextureIDs may be null here during startup if Layer.java's draw method + // failed to acquire the transaction lock and call performUpdates. + if (!initialized()) + return; + + float txl, txr, txb, txt; + + Rect position = getPosition(); + + float bw = mBufferSize.width; + float bh = mBufferSize.height; + float iw = mImageSize.width; + float ih = mImageSize.height; + + float pw = context.pageRect.width(); + float ph = context.pageRect.height(); + + float vl = context.viewport.left; + float vr = context.viewport.right; + float vt = context.viewport.top; + float vb = context.viewport.bottom; + + float vw = vr - vl; + float vh = vb - vt; + + txl = (iw/bw) * (vl / pw); + txr = (iw/bw) * (vr / pw); + txt = 1.0f - ((ih/bh) * (vt / ph)); + txb = 1.0f - ((ih/bh) * (vb / ph)); + + float[] coords = { + 0.0f, 0.0f, 0.0f, txl, txb, + 0.0f, 1.0f, 0.0f, txl, txt, + 1.0f, 0.0f, 0.0f, txr, txb, + 1.0f, 1.0f, 0.0f, txr, txt, + }; + + FloatBuffer coordBuffer = context.coordBuffer; + int positionHandle = context.positionHandle; + int textureHandle = context.textureHandle; + + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, getTextureID()); + + // Make sure we are at position zero in the buffer + coordBuffer.position(0); + coordBuffer.put(coords); + + // Vertex coordinates are x,y,z starting at position 0 into the buffer. + coordBuffer.position(0); + GLES20.glVertexAttribPointer(positionHandle, 3, GLES20.GL_FLOAT, false, 20, coordBuffer); + + // Texture coordinates are texture_x, texture_y starting at position 3 into the buffer. + coordBuffer.position(3); + GLES20.glVertexAttribPointer(textureHandle, 2, GLES20.GL_FLOAT, false, 20, coordBuffer); + GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); } /** A Cairo image that simply saves a buffer of pixel data. */ commit ebc25c427936708b8e32e563d6735575d6ac9f92 Author: Tomaž Vajngerl <tomaz.vajng...@collabora.com> Date: Sat Sep 27 23:38:19 2014 +0200 android: replace with ImmutableViewportMetrics (Fennec import) Change-Id: I46509f8be4dc49dac45eb98059dad25e150988dd diff --git a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOEvent.java b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOEvent.java index f16b2da..80210a7 100644 --- a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOEvent.java +++ b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOEvent.java @@ -2,8 +2,8 @@ package org.libreoffice; import android.graphics.Rect; +import org.mozilla.gecko.gfx.ImmutableViewportMetrics; import org.mozilla.gecko.gfx.IntSize; -import org.mozilla.gecko.gfx.ViewportMetrics; public class LOEvent { @@ -15,7 +15,7 @@ public class LOEvent { public static final int LOAD = 6; public int mType; - private ViewportMetrics mViewportMetrics; + private ImmutableViewportMetrics mViewportMetrics; private String mTypeString; private int mPartIndex; private String mFilename; @@ -35,7 +35,7 @@ public class LOEvent { mTypeString = "Tile size"; } - public LOEvent(int type, ViewportMetrics viewportMetrics) { + public LOEvent(int type, ImmutableViewportMetrics viewportMetrics) { mType = type; mTypeString = "Viewport"; mViewportMetrics = viewportMetrics; @@ -64,7 +64,7 @@ public class LOEvent { return new LOEvent(TILE_SIZE, tileSize); } - public static LOEvent viewport(ViewportMetrics viewportMetrics) { + public static LOEvent viewport(ImmutableViewportMetrics viewportMetrics) { return new LOEvent(VIEWPORT, viewportMetrics); } @@ -80,7 +80,7 @@ public class LOEvent { return mTypeString; } - public ViewportMetrics getViewport() { + public ImmutableViewportMetrics getViewport() { return mViewportMetrics; } diff --git a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java index ddc18d4..f808f57 100644 --- a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java +++ b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java @@ -7,8 +7,8 @@ import android.util.DisplayMetrics; import android.util.Log; import org.mozilla.gecko.gfx.GeckoLayerClient; +import org.mozilla.gecko.gfx.ImmutableViewportMetrics; import org.mozilla.gecko.gfx.LayerController; -import org.mozilla.gecko.gfx.ViewportMetrics; import java.util.concurrent.LinkedBlockingQueue; @@ -18,7 +18,7 @@ public class LOKitThread extends Thread { public LinkedBlockingQueue<LOEvent> mEventQueue = new LinkedBlockingQueue<LOEvent>(); private LibreOfficeMainActivity mApplication; private TileProvider mTileProvider; - private ViewportMetrics mViewportMetrics; + private ImmutableViewportMetrics mViewportMetrics; private boolean mCheckboardImageSet = false; private GeckoLayerClient mLayerClient; private LayerController mController; @@ -33,8 +33,8 @@ public class LOKitThread extends Thread { RectF rect = new RectF(0, 0, pageWidth, pageHeight); DisplayMetrics displayMetrics = LibreOfficeMainActivity.mAppContext.getResources().getDisplayMetrics(); - mViewportMetrics = new ViewportMetrics(displayMetrics); - mViewportMetrics.setPageRect(rect, rect); + mViewportMetrics = new ImmutableViewportMetrics(displayMetrics); + mViewportMetrics = mViewportMetrics.setPageRect(rect, rect); GeckoLayerClient layerClient = mApplication.getLayerClient(); 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 e3f5793..e847c01 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 @@ -65,10 +65,10 @@ public class GeckoLayerClient implements LayerView.Listener { private MultiTileLayer mRootLayer; /* The viewport that Gecko is currently displaying. */ - private ViewportMetrics mGeckoViewport; + private ImmutableViewportMetrics mGeckoViewport; /* The viewport that Gecko will display when drawing is finished */ - private ViewportMetrics mNewGeckoViewport; + private ImmutableViewportMetrics mNewGeckoViewport; private Context mContext; private boolean mPendingViewportAdjust; private boolean mViewportSizeChanged; @@ -109,7 +109,7 @@ public class GeckoLayerClient implements LayerView.Listener { } - public void endDrawing(ViewportMetrics viewportMetrics) { + public void endDrawing(ImmutableViewportMetrics viewportMetrics) { synchronized (mLayerController) { try { mNewGeckoViewport = viewportMetrics; @@ -128,19 +128,18 @@ public class GeckoLayerClient implements LayerView.Listener { // java is the One True Source of this information, and allowing JS // to override can lead to race conditions where this data gets clobbered. FloatSize viewportSize = mLayerController.getViewportSize(); - mGeckoViewport = mNewGeckoViewport; - mGeckoViewport.setSize(viewportSize); + mGeckoViewport = mNewGeckoViewport.setViewportSize(viewportSize.width, viewportSize.height); RectF position = mGeckoViewport.getViewport(); mRootLayer.setPosition(RectUtils.round(position)); - mRootLayer.setResolution(mGeckoViewport.getZoomFactor()); + mRootLayer.setResolution(mGeckoViewport.zoomFactor); Log.e(LOGTAG, "### updateViewport onlyUpdatePageSize=" + onlyUpdatePageSize + " getTileViewport " + mGeckoViewport); if (onlyUpdatePageSize) { // Don't adjust page size when zooming unless zoom levels are // approximately equal. - if (FloatUtils.fuzzyEquals(mLayerController.getViewportMetrics().zoomFactor, mGeckoViewport.getZoomFactor())) { + if (FloatUtils.fuzzyEquals(mLayerController.getViewportMetrics().zoomFactor, mGeckoViewport.zoomFactor)) { mLayerController.setPageRect(mGeckoViewport.getPageRect(), mGeckoViewport.getCssPageRect()); } } else { @@ -193,8 +192,7 @@ public class GeckoLayerClient implements LayerView.Listener { void adjustViewport(DisplayPortMetrics displayPort) { ImmutableViewportMetrics metrics = mLayerController.getViewportMetrics(); - ViewportMetrics clampedMetrics = new ViewportMetrics(metrics); - clampedMetrics.setViewport(clampedMetrics.getClampedViewport()); + ImmutableViewportMetrics clampedMetrics = metrics.clamp(); if (displayPort == null) { displayPort = DisplayPortCalculator.calculate(metrics, @@ -244,7 +242,7 @@ public class GeckoLayerClient implements LayerView.Listener { } } - public ViewportMetrics getGeckoViewportMetrics() { + public ImmutableViewportMetrics getGeckoViewportMetrics() { return mGeckoViewport; } 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 index 5403c80..35b4175 100644 --- a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/ImmutableViewportMetrics.java +++ b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/ImmutableViewportMetrics.java @@ -7,6 +7,9 @@ package org.mozilla.gecko.gfx; import android.graphics.PointF; import android.graphics.RectF; +import android.util.DisplayMetrics; + +import org.mozilla.gecko.util.FloatUtils; /** * ImmutableViewportMetrics are used to store the viewport metrics @@ -31,26 +34,33 @@ public class ImmutableViewportMetrics { public final float viewportRectBottom; public final float zoomFactor; - public ImmutableViewportMetrics(ViewportMetrics m) { - RectF viewportRect = m.getViewport(); - viewportRectLeft = viewportRect.left; - viewportRectTop = viewportRect.top; - viewportRectRight = viewportRect.right; - viewportRectBottom = viewportRect.bottom; - - RectF pageRect = m.getPageRect(); - pageRectLeft = pageRect.left; - pageRectTop = pageRect.top; - pageRectRight = pageRect.right; - pageRectBottom = pageRect.bottom; - - RectF cssPageRect = m.getCssPageRect(); - cssPageRectLeft = cssPageRect.left; - cssPageRectTop = cssPageRect.top; - cssPageRectRight = cssPageRect.right; - cssPageRectBottom = cssPageRect.bottom; + public ImmutableViewportMetrics(DisplayMetrics metrics) { + viewportRectLeft = pageRectLeft = cssPageRectLeft = 0; + viewportRectTop = pageRectTop = cssPageRectTop = 0; + viewportRectRight = pageRectRight = cssPageRectRight = metrics.widthPixels; + viewportRectBottom = pageRectBottom = cssPageRectBottom = metrics.heightPixels; + zoomFactor = 1.0f; + } - zoomFactor = m.getZoomFactor(); + private ImmutableViewportMetrics(float aPageRectLeft, float aPageRectTop, + float aPageRectRight, float aPageRectBottom, float aCssPageRectLeft, + float aCssPageRectTop, float aCssPageRectRight, float aCssPageRectBottom, + float aViewportRectLeft, float aViewportRectTop, float aViewportRectRight, + float aViewportRectBottom, float aZoomFactor) + { + pageRectLeft = aPageRectLeft; + pageRectTop = aPageRectTop; + pageRectRight = aPageRectRight; + pageRectBottom = aPageRectBottom; + cssPageRectLeft = aCssPageRectLeft; + cssPageRectTop = aCssPageRectTop; + cssPageRectRight = aCssPageRectRight; + cssPageRectBottom = aCssPageRectBottom; + viewportRectLeft = aViewportRectLeft; + viewportRectTop = aViewportRectTop; + viewportRectRight = aViewportRectRight; + viewportRectBottom = aViewportRectBottom; + zoomFactor = aZoomFactor; } public float getWidth() { @@ -61,8 +71,6 @@ public class ImmutableViewportMetrics { return viewportRectBottom - viewportRectTop; } - // some helpers to make ImmutableViewportMetrics act more like ViewportMetrics - public PointF getOrigin() { return new PointF(viewportRectLeft, viewportRectTop); } @@ -98,6 +106,126 @@ public class ImmutableViewportMetrics { return new RectF(cssPageRectLeft, cssPageRectTop, cssPageRectRight, cssPageRectBottom); } + /* + * Returns the viewport metrics that represent a linear transition between "this" and "to" at + * time "t", which is on the scale [0, 1). This function interpolates all values stored in + * the viewport metrics. + */ + public ImmutableViewportMetrics interpolate(ImmutableViewportMetrics to, float t) { + return new ImmutableViewportMetrics( + FloatUtils.interpolate(pageRectLeft, to.pageRectLeft, t), + FloatUtils.interpolate(pageRectTop, to.pageRectTop, t), + FloatUtils.interpolate(pageRectRight, to.pageRectRight, t), + FloatUtils.interpolate(pageRectBottom, to.pageRectBottom, t), + FloatUtils.interpolate(cssPageRectLeft, to.cssPageRectLeft, t), + FloatUtils.interpolate(cssPageRectTop, to.cssPageRectTop, t), + FloatUtils.interpolate(cssPageRectRight, to.cssPageRectRight, t), + FloatUtils.interpolate(cssPageRectBottom, to.cssPageRectBottom, t), + FloatUtils.interpolate(viewportRectLeft, to.viewportRectLeft, t), + FloatUtils.interpolate(viewportRectTop, to.viewportRectTop, t), + FloatUtils.interpolate(viewportRectRight, to.viewportRectRight, t), + FloatUtils.interpolate(viewportRectBottom, to.viewportRectBottom, t), + FloatUtils.interpolate(zoomFactor, to.zoomFactor, t)); + } + + public ImmutableViewportMetrics setViewportSize(float width, float height) { + return new ImmutableViewportMetrics( + pageRectLeft, pageRectTop, pageRectRight, pageRectBottom, + cssPageRectLeft, cssPageRectTop, cssPageRectRight, cssPageRectBottom, + viewportRectLeft, viewportRectTop, viewportRectLeft + width, viewportRectTop + height, + zoomFactor); + } + + public ImmutableViewportMetrics setViewportOrigin(float newOriginX, float newOriginY) { + return new ImmutableViewportMetrics( + pageRectLeft, pageRectTop, pageRectRight, pageRectBottom, + cssPageRectLeft, cssPageRectTop, cssPageRectRight, cssPageRectBottom, + newOriginX, newOriginY, newOriginX + getWidth(), newOriginY + getHeight(), + zoomFactor); + } + + public ImmutableViewportMetrics setZoomFactor(float newZoomFactor) { + return new ImmutableViewportMetrics( + pageRectLeft, pageRectTop, pageRectRight, pageRectBottom, + cssPageRectLeft, cssPageRectTop, cssPageRectRight, cssPageRectBottom, + viewportRectLeft, viewportRectTop, viewportRectRight, viewportRectBottom, + newZoomFactor); + } + + public ImmutableViewportMetrics offsetViewportBy(float dx, float dy) { + return setViewportOrigin(viewportRectLeft + dx, viewportRectTop + dy); + } + + public ImmutableViewportMetrics setPageRect(RectF pageRect, RectF cssPageRect) { + return new ImmutableViewportMetrics( + pageRect.left, pageRect.top, pageRect.right, pageRect.bottom, + cssPageRect.left, cssPageRect.top, cssPageRect.right, cssPageRect.bottom, + viewportRectLeft, viewportRectTop, viewportRectRight, viewportRectBottom, + zoomFactor); + } + + /* This will set the zoom factor and re-scale page-size and viewport offset + * accordingly. The given focus will remain at the same point on the screen + * after scaling. + */ + public ImmutableViewportMetrics scaleTo(float newZoomFactor, PointF focus) { + // cssPageRect* is invariant, since we're setting the scale factor + // here. The page rect is based on the CSS page rect. + float newPageRectLeft = cssPageRectLeft * newZoomFactor; + float newPageRectTop = cssPageRectTop * newZoomFactor; + float newPageRectRight = cssPageRectLeft + ((cssPageRectRight - cssPageRectLeft) * newZoomFactor); + float newPageRectBottom = cssPageRectTop + ((cssPageRectBottom - cssPageRectTop) * newZoomFactor); + + PointF origin = getOrigin(); + origin.offset(focus.x, focus.y); + origin = PointUtils.scale(origin, newZoomFactor / zoomFactor); + origin.offset(-focus.x, -focus.y); + + return new ImmutableViewportMetrics( + newPageRectLeft, newPageRectTop, newPageRectRight, newPageRectBottom, + cssPageRectLeft, cssPageRectTop, cssPageRectRight, cssPageRectBottom, + origin.x, origin.y, origin.x + getWidth(), origin.y + getHeight(), + newZoomFactor); + } + + /** Clamps the viewport to remain within the page rect. */ + public ImmutableViewportMetrics clamp() { + RectF newViewport = getViewport(); + + // The viewport bounds ought to never exceed the page bounds. + if (newViewport.right > pageRectRight) + newViewport.offset(pageRectRight - newViewport.right, 0); + if (newViewport.left < pageRectLeft) + newViewport.offset(pageRectLeft - newViewport.left, 0); + + if (newViewport.bottom > pageRectBottom) + newViewport.offset(0, pageRectBottom - newViewport.bottom); + if (newViewport.top < pageRectTop) + newViewport.offset(0, pageRectTop - newViewport.top); + + return new ImmutableViewportMetrics( + pageRectLeft, pageRectTop, pageRectRight, pageRectBottom, + cssPageRectLeft, cssPageRectTop, cssPageRectRight, cssPageRectBottom, + newViewport.left, newViewport.top, newViewport.right, newViewport.bottom, + zoomFactor); + } + + public boolean fuzzyEquals(ImmutableViewportMetrics other) { + return FloatUtils.fuzzyEquals(pageRectLeft, other.pageRectLeft) + && FloatUtils.fuzzyEquals(pageRectTop, other.pageRectTop) + && FloatUtils.fuzzyEquals(pageRectRight, other.pageRectRight) + && FloatUtils.fuzzyEquals(pageRectBottom, other.pageRectBottom) + && FloatUtils.fuzzyEquals(cssPageRectLeft, other.cssPageRectLeft) + && FloatUtils.fuzzyEquals(cssPageRectTop, other.cssPageRectTop) + && FloatUtils.fuzzyEquals(cssPageRectRight, other.cssPageRectRight) + && FloatUtils.fuzzyEquals(cssPageRectBottom, other.cssPageRectBottom) + && FloatUtils.fuzzyEquals(viewportRectLeft, other.viewportRectLeft) + && FloatUtils.fuzzyEquals(viewportRectTop, other.viewportRectTop) + && FloatUtils.fuzzyEquals(viewportRectRight, other.viewportRectRight) + && FloatUtils.fuzzyEquals(viewportRectBottom, other.viewportRectBottom) + && FloatUtils.fuzzyEquals(zoomFactor, other.zoomFactor); + } + @Override public String toString() { return "ImmutableViewportMetrics v=(" + viewportRectLeft + "," + viewportRectTop + "," 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 ca02e1c..2e6a4a0 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 @@ -67,7 +67,7 @@ public class LayerController implements PanZoomTarget { mContext = context; mForceRedraw = true; DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics(); - mViewportMetrics = new ImmutableViewportMetrics(new ViewportMetrics(displayMetrics)); + mViewportMetrics = new ImmutableViewportMetrics(displayMetrics); mPanZoomController = new PanZoomController(this); mView = new LayerView(context, this); mCheckerboardShouldShowChecks = true; @@ -127,9 +127,7 @@ public class LayerController implements PanZoomTarget { * result in an infinite loop. */ public void setViewportSize(FloatSize size) { - ViewportMetrics viewportMetrics = new ViewportMetrics(mViewportMetrics); - viewportMetrics.setSize(size); - mViewportMetrics = new ImmutableViewportMetrics(viewportMetrics); + mViewportMetrics = mViewportMetrics.setViewportSize(size.width, size.height); if (mLayerClient != null) { mLayerClient.viewportSizeChanged(); @@ -144,9 +142,7 @@ public class LayerController implements PanZoomTarget { if (mViewportMetrics.getCssPageRect().equals(cssRect)) return; - ViewportMetrics viewportMetrics = new ViewportMetrics(mViewportMetrics); - viewportMetrics.setPageRect(rect, cssRect); - mViewportMetrics = new ImmutableViewportMetrics(viewportMetrics); + mViewportMetrics = mViewportMetrics.setPageRect(rect, cssRect); // Page size is owned by the layer client, so no need to notify it of // this change. @@ -163,20 +159,19 @@ public class LayerController implements PanZoomTarget { * Sets the entire viewport metrics at once. * You must hold the monitor while calling this. */ - public void setViewportMetrics(ViewportMetrics viewport) { - mViewportMetrics = new ImmutableViewportMetrics(viewport); + public void setViewportMetrics(ImmutableViewportMetrics viewport) { + mViewportMetrics = viewport; mView.requestRender(); notifyLayerClientOfGeometryChange(); } - public void setAnimationTarget(ViewportMetrics viewport) { + public void setAnimationTarget(ImmutableViewportMetrics viewport) { if (mLayerClient != null) { // We know what the final viewport of the animation is going to be, so // immediately request a draw of that area by setting the display port // accordingly. This way we should have the content pre-rendered by the // time the animation is done. - ImmutableViewportMetrics metrics = new ImmutableViewportMetrics(viewport); - DisplayPortMetrics displayPort = DisplayPortCalculator.calculate(metrics, null); + DisplayPortMetrics displayPort = DisplayPortCalculator.calculate(viewport, null); mLayerClient.adjustViewport(displayPort); } } @@ -232,9 +227,9 @@ public class LayerController implements PanZoomTarget { ImmutableViewportMetrics viewportMetrics = mViewportMetrics; PointF origin = viewportMetrics.getOrigin(); float zoom = viewportMetrics.zoomFactor; - ViewportMetrics geckoViewport = mLayerClient.getGeckoViewportMetrics(); + ImmutableViewportMetrics geckoViewport = mLayerClient.getGeckoViewportMetrics(); PointF geckoOrigin = geckoViewport.getOrigin(); - float geckoZoom = geckoViewport.getZoomFactor(); + float geckoZoom = geckoViewport.zoomFactor; // 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. diff --git a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/PointUtils.java b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/PointUtils.java index cdacc31..4eb07a3 100644 --- a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/PointUtils.java +++ b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/PointUtils.java @@ -10,7 +10,6 @@ import android.graphics.PointF; import org.json.JSONException; import org.json.JSONObject; -import org.mozilla.gecko.util.FloatUtils; public final class PointUtils { public static PointF add(PointF one, PointF two) { @@ -29,19 +28,10 @@ public final class PointUtils { return new Point(Math.round(point.x), Math.round(point.y)); } - /* Returns a new point that is a linear interpolation between start and end points. weight conrols the weighting - * of each of the original points (weight = 1 returns endPoint, weight = 0 returns startPoint) - */ - public static PointF interpolate(PointF startPoint, PointF endPoint, float weight) { - float x = FloatUtils.interpolate(startPoint.x, endPoint.x, weight); - float y = FloatUtils.interpolate(startPoint.y, endPoint.y, weight); - return new PointF(x, y); - } - - /* Computes the magnitude of the given vector. */ - public static float distance(PointF point) { + /* Computes the magnitude of the given vector. */ + public static float distance(PointF point) { return (float)Math.sqrt(point.x * point.x + point.y * point.y); - } + } /** Computes the scalar distance between two points. */ public static float distance(PointF one, PointF two) { diff --git a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/RectUtils.java b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/RectUtils.java index 92ea78f..1608e91 100644 --- a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/RectUtils.java +++ b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/RectUtils.java @@ -10,22 +10,10 @@ import android.graphics.PointF; import android.graphics.Rect; import android.graphics.RectF; -import org.json.JSONException; -import org.json.JSONObject; import org.mozilla.gecko.util.FloatUtils; public final class RectUtils { - public static Rect create(JSONObject json) { - try { - int x = json.getInt("x"); - int y = json.getInt("y"); - int width = json.getInt("width"); - int height = json.getInt("height"); - return new Rect(x, y, x + width, y + height); - } catch (JSONException e) { - throw new RuntimeException(e); - } - } + private RectUtils() {} public static RectF expand(RectF rect, float moreWidth, float moreHeight) { float halfMoreWidth = moreWidth / 2; @@ -63,8 +51,14 @@ public final class RectUtils { /** Returns the nearest integer rect of the given rect. */ public static Rect round(RectF rect) { - return new Rect(Math.round(rect.left), Math.round(rect.top), - Math.round(rect.right), Math.round(rect.bottom)); + Rect r = new Rect(); + round(rect, r); + return r; + } + + public static void round(RectF rect, Rect dest) { + dest.set(Math.round(rect.left), Math.round(rect.top), + Math.round(rect.right), Math.round(rect.bottom)); } public static Rect roundIn(RectF rect) { @@ -84,17 +78,6 @@ public final class RectUtils { return new PointF(rect.left, rect.top); } - /* - * Returns the rect that represents a linear transition between `from` and `to` at time `t`, - * which is on the scale [0, 1). - */ - public static RectF interpolate(RectF from, RectF to, float t) { - return new RectF(FloatUtils.interpolate(from.left, to.left, t), - FloatUtils.interpolate(from.top, to.top, t), - FloatUtils.interpolate(from.right, to.right, t), - FloatUtils.interpolate(from.bottom, to.bottom, t)); - } - public static boolean fuzzyEquals(RectF a, RectF b) { if (a == null && b == null) return true; 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 789bb0b..f8b5c2e0 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 @@ -11,7 +11,6 @@ import android.util.DisplayMetrics; import org.json.JSONException; import org.json.JSONObject; -import org.mozilla.gecko.util.FloatUtils; /** * ViewportMetrics manages state and contains some utility functions related to @@ -76,6 +75,16 @@ public class ViewportMetrics { mZoomFactor = zoom; } + public ViewportMetrics(float x, float y, float width, float height, + float pageLeft, float pageTop, float pageRight, float pageBottom, + float cssPageLeft, float cssPageTop, float cssPageRight, float cssPageBottom, + float zoom) { + mPageRect = new RectF(pageLeft, pageTop, pageRight, pageBottom); + mCssPageRect = new RectF(cssPageLeft, cssPageTop, cssPageRight, cssPageBottom); + mViewportRect = new RectF(x, y, x + width, y + height); + mZoomFactor = zoom; + } + public PointF getOrigin() { return new PointF(mViewportRect.left, mViewportRect.top); } @@ -92,24 +101,6 @@ public class ViewportMetrics { return RectUtils.scale(mViewportRect, 1/mZoomFactor); } - /** Returns the viewport rectangle, clamped within the page-size. */ - public RectF getClampedViewport() { - RectF clampedViewport = new RectF(mViewportRect); - - // The viewport bounds ought to never exceed the page bounds. - if (clampedViewport.right > mPageRect.right) - clampedViewport.offset(mPageRect.right - clampedViewport.right, 0); - if (clampedViewport.left < mPageRect.left) - clampedViewport.offset(mPageRect.left - clampedViewport.left, 0); - - if (clampedViewport.bottom > mPageRect.bottom) - clampedViewport.offset(0, mPageRect.bottom - clampedViewport.bottom); - if (clampedViewport.top < mPageRect.top) - clampedViewport.offset(0, mPageRect.top - clampedViewport.top); - - return clampedViewport; - } - public RectF getPageRect() { return mPageRect; } @@ -146,48 +137,6 @@ public class ViewportMetrics { mZoomFactor = zoomFactor; } - /* This will set the zoom factor and re-scale page-size and viewport offset - * accordingly. The given focus will remain at the same point on the screen - * after scaling. - */ - public void scaleTo(float newZoomFactor, PointF focus) { - // mCssPageRect is invariant, since we're setting the scale factor - // here. The page rect is based on the CSS page rect. - mPageRect = RectUtils.scale(mCssPageRect, newZoomFactor); - - float scaleFactor = newZoomFactor / mZoomFactor; - PointF origin = getOrigin(); - - origin.offset(focus.x, focus.y); - origin = PointUtils.scale(origin, scaleFactor); - origin.offset(-focus.x, -focus.y); - - setOrigin(origin); - - mZoomFactor = newZoomFactor; - } - - /* - * Returns the viewport metrics that represent a linear transition between `from` and `to` at - * time `t`, which is on the scale [0, 1). This function interpolates the viewport rect, the - * page size, the offset, and the zoom factor. - */ - public ViewportMetrics interpolate(ViewportMetrics to, float t) { - ViewportMetrics result = new ViewportMetrics(this); - result.mPageRect = RectUtils.interpolate(mPageRect, to.mPageRect, t); - result.mCssPageRect = RectUtils.interpolate(mCssPageRect, to.mCssPageRect, t); - result.mZoomFactor = FloatUtils.interpolate(mZoomFactor, to.mZoomFactor, t); - result.mViewportRect = RectUtils.interpolate(mViewportRect, to.mViewportRect, t); - return result; - } - - public boolean fuzzyEquals(ViewportMetrics other) { - return RectUtils.fuzzyEquals(mPageRect, other.mPageRect) - && RectUtils.fuzzyEquals(mCssPageRect, other.mCssPageRect) - && RectUtils.fuzzyEquals(mViewportRect, other.mViewportRect) - && FloatUtils.fuzzyEquals(mZoomFactor, other.mZoomFactor); - } - public String toJSON() { // Round off height and width. Since the height and width are the size of the screen, it // makes no sense to send non-integer coordinates to Gecko. 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 02e45da..555c7ebe 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 @@ -16,7 +16,6 @@ import org.libreoffice.LOKitShell; import org.libreoffice.LibreOfficeMainActivity; import org.mozilla.gecko.ZoomConstraints; import org.mozilla.gecko.gfx.ImmutableViewportMetrics; -import org.mozilla.gecko.gfx.ViewportMetrics; import org.mozilla.gecko.util.FloatUtils; import java.util.Timer; @@ -118,10 +117,6 @@ public class PanZoomController return mTarget.getViewportMetrics(); } - private ViewportMetrics getMutableMetrics() { - return new ViewportMetrics(getMetrics()); - } - // for debugging bug 713011; it can be taken out once that is resolved. private void checkMainThread() { if (mMainThread != Thread.currentThread()) { @@ -195,8 +190,8 @@ public class PanZoomController public void pageRectUpdated() { if (mState == PanZoomState.NOTHING) { synchronized (mTarget.getLock()) { - ViewportMetrics validated = getValidViewportMetrics(); - if (! getMutableMetrics().fuzzyEquals(validated)) { + ImmutableViewportMetrics validated = getValidViewportMetrics(); + if (!getMetrics().fuzzyEquals(validated)) { // page size changed such that we are now in overscroll. snap to the // the nearest valid viewport mTarget.setViewportMetrics(validated); @@ -421,13 +416,9 @@ public class PanZoomController updatePosition(); } - private void scrollBy(PointF point) { - ViewportMetrics viewportMetrics = getMutableMetrics(); - PointF origin = viewportMetrics.getOrigin(); - origin.offset(point.x, point.y); - viewportMetrics.setOrigin(origin); - - mTarget.setViewportMetrics(viewportMetrics); + private void scrollBy(float dx, float dy) { + ImmutableViewportMetrics scrolled = getMetrics().offsetViewportBy(dx, dy); + mTarget.setViewportMetrics(scrolled); } private void fling() { @@ -443,10 +434,10 @@ public class PanZoomController } /* Performs a bounce-back animation to the given viewport metrics. */ - private void bounce(ViewportMetrics metrics) { + private void bounce(ImmutableViewportMetrics metrics) { stopAnimationTimer(); - ViewportMetrics bounceStartMetrics = getMutableMetrics(); + ImmutableViewportMetrics bounceStartMetrics = getMetrics(); if (bounceStartMetrics.fuzzyEquals(metrics)) { setState(PanZoomState.NOTHING); return; @@ -520,7 +511,7 @@ public class PanZoomController } if (! mSubscroller.scrollBy(displacement)) { synchronized (mTarget.getLock()) { - scrollBy(displacement); + scrollBy(displacement.x, displacement.y); } } } @@ -558,10 +549,10 @@ public class PanZoomController * The viewport metrics that represent the start and end of the bounce-back animation, * respectively. */ - private ViewportMetrics mBounceStartMetrics; - private ViewportMetrics mBounceEndMetrics; + private ImmutableViewportMetrics mBounceStartMetrics; + private ImmutableViewportMetrics mBounceEndMetrics; - BounceRunnable(ViewportMetrics startMetrics, ViewportMetrics endMetrics) { + BounceRunnable(ImmutableViewportMetrics startMetrics, ImmutableViewportMetrics endMetrics) { mBounceStartMetrics = startMetrics; mBounceEndMetrics = endMetrics; } @@ -593,7 +584,7 @@ public class PanZoomController private void advanceBounce() { synchronized (mTarget.getLock()) { float t = easeOut(mBounceFrame * Axis.MS_PER_FRAME / 256f); - ViewportMetrics newMetrics = mBounceStartMetrics.interpolate(mBounceEndMetrics, t); + ImmutableViewportMetrics newMetrics = mBounceStartMetrics.interpolate(mBounceEndMetrics, t); mTarget.setViewportMetrics(newMetrics); mBounceFrame++; } @@ -667,13 +658,13 @@ public class PanZoomController } /* Returns the nearest viewport metrics with no overscroll visible. */ - private ViewportMetrics getValidViewportMetrics() { - return getValidViewportMetrics(getMutableMetrics()); + private ImmutableViewportMetrics getValidViewportMetrics() { + return getValidViewportMetrics(getMetrics()); } - private ViewportMetrics getValidViewportMetrics(ViewportMetrics viewportMetrics) { + private ImmutableViewportMetrics getValidViewportMetrics(ImmutableViewportMetrics viewportMetrics) { /* First, we adjust the zoom factor so that we can make no overscrolled area visible. */ - float zoomFactor = viewportMetrics.getZoomFactor(); + float zoomFactor = viewportMetrics.zoomFactor; RectF pageRect = viewportMetrics.getPageRect(); RectF viewport = viewportMetrics.getViewport(); @@ -718,14 +709,14 @@ public class PanZoomController // by different scale factors, we end up scrolled to the end on one axis // after applying the scale PointF center = new PointF(focusX, focusY); - viewportMetrics.scaleTo(minZoomFactor, center); + viewportMetrics = viewportMetrics.scaleTo(minZoomFactor, center); } else if (zoomFactor > maxZoomFactor) { PointF center = new PointF(viewport.width() / 2.0f, viewport.height() / 2.0f); - viewportMetrics.scaleTo(maxZoomFactor, center); + viewportMetrics = viewportMetrics.scaleTo(maxZoomFactor, center); } /* Now we pan to the right origin. */ - viewportMetrics.setViewport(viewportMetrics.getClampedViewport()); + viewportMetrics = viewportMetrics.clamp(); return viewportMetrics; } @@ -826,8 +817,8 @@ public class PanZoomController newZoomFactor = maxZoomFactor + excessZoom; } - scrollBy(new PointF(mLastZoomFocus.x - detector.getFocusX(), - mLastZoomFocus.y - detector.getFocusY())); + scrollBy(mLastZoomFocus.x - detector.getFocusX(), + mLastZoomFocus.y - detector.getFocusY()); PointF focus = new PointF(detector.getFocusX(), detector.getFocusY()); scaleWithFocus(newZoomFactor, focus); } @@ -854,8 +845,8 @@ public class PanZoomController * scale operation. You must hold the monitor while calling this. */ private void scaleWithFocus(float zoomFactor, PointF focus) { - ViewportMetrics viewportMetrics = getMutableMetrics(); - viewportMetrics.scaleTo(zoomFactor, focus); + ImmutableViewportMetrics viewportMetrics = getMetrics(); + viewportMetrics = viewportMetrics.scaleTo(zoomFactor, focus); mTarget.setViewportMetrics(viewportMetrics); } @@ -931,10 +922,11 @@ public class PanZoomController float finalZoom = viewport.width() / zoomToRect.width(); - ViewportMetrics finalMetrics = getMutableMetrics(); - finalMetrics.setOrigin(new PointF(zoomToRect.left * finalMetrics.getZoomFactor(), - zoomToRect.top * finalMetrics.getZoomFactor())); - finalMetrics.scaleTo(finalZoom, new PointF(0.0f, 0.0f)); + ImmutableViewportMetrics finalMetrics = getMetrics(); + finalMetrics = finalMetrics.setViewportOrigin( + zoomToRect.left * finalMetrics.zoomFactor, + zoomToRect.top * finalMetrics.zoomFactor); + finalMetrics = finalMetrics.scaleTo(finalZoom, new PointF(0.0f, 0.0f)); // 2. now run getValidViewportMetrics on it, so that the target viewport is // clamped down to prevent overscroll, over-zoom, and other bad conditions. diff --git a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/ui/PanZoomTarget.java b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/ui/PanZoomTarget.java index 3ebc4f1..fdac874 100644 --- a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/ui/PanZoomTarget.java +++ b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/ui/PanZoomTarget.java @@ -9,14 +9,13 @@ import android.graphics.PointF; import org.mozilla.gecko.ZoomConstraints; import org.mozilla.gecko.gfx.ImmutableViewportMetrics; -import org.mozilla.gecko.gfx.ViewportMetrics; public interface PanZoomTarget { public ImmutableViewportMetrics getViewportMetrics(); public ZoomConstraints getZoomConstraints(); - public void setAnimationTarget(ViewportMetrics viewport); - public void setViewportMetrics(ViewportMetrics viewport); + public void setAnimationTarget(ImmutableViewportMetrics viewport); + public void setViewportMetrics(ImmutableViewportMetrics viewport); public void setForceRedraw(); public boolean post(Runnable action); commit f66ff689d0b3ba5196cac717c7228f541d853e3f Author: Tomaž Vajngerl <tomaz.vajng...@collabora.com> Date: Sat Sep 27 22:54:20 2014 +0200 android: Improve panning smoothness (Fennec import) Change-Id: I3983709651548eb97e588ebe2c2de608a4a4dfc7 diff --git a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/ui/Axis.java b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/ui/Axis.java index fcdcd72..7ae8084 100644 --- a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/ui/Axis.java +++ b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/ui/Axis.java @@ -6,8 +6,8 @@ package org.mozilla.gecko.ui; import android.util.Log; +import android.view.View; -import org.json.JSONArray; import org.mozilla.gecko.util.FloatUtils; import java.util.Map; @@ -23,7 +23,6 @@ abstract class Axis { private static final String PREF_SCROLLING_FRICTION_SLOW = "ui.scrolling.friction_slow"; private static final String PREF_SCROLLING_FRICTION_FAST = "ui.scrolling.friction_fast"; - private static final String PREF_SCROLLING_VELOCITY_THRESHOLD = "ui.scrolling.velocity_threshold"; private static final String PREF_SCROLLING_MAX_EVENT_ACCELERATION = "ui.scrolling.max_event_acceleration"; private static final String PREF_SCROLLING_OVERSCROLL_DECEL_RATE = "ui.scrolling.overscroll_decel_rate"; private static final String PREF_SCROLLING_OVERSCROLL_SNAP_LIMIT = "ui.scrolling.overscroll_snap_limit"; @@ -59,22 +58,22 @@ abstract class Axis { return (value == null || value < 0 ? defaultValue : value); } - static void addPrefNames(JSONArray prefs) { - prefs.put(PREF_SCROLLING_FRICTION_FAST); - prefs.put(PREF_SCROLLING_FRICTION_SLOW); - prefs.put(PREF_SCROLLING_VELOCITY_THRESHOLD); - prefs.put(PREF_SCROLLING_MAX_EVENT_ACCELERATION); - prefs.put(PREF_SCROLLING_OVERSCROLL_DECEL_RATE); - prefs.put(PREF_SCROLLING_OVERSCROLL_SNAP_LIMIT); - prefs.put(PREF_SCROLLING_MIN_SCROLLABLE_DISTANCE); + static final float MS_PER_FRAME = 4.0f; + private static final float FRAMERATE_MULTIPLIER = (1000f/60f) / MS_PER_FRAME; + + // The values we use for friction are based on a 16.6ms frame, adjust them to MS_PER_FRAME: + // FRICTION^1 = FRICTION_ADJUSTED^(16/MS_PER_FRAME) + // FRICTION_ADJUSTED = e ^ ((ln(FRICTION))/FRAMERATE_MULTIPLIER) + static float getFrameAdjustedFriction(float baseFriction) { + return (float)Math.pow(Math.E, (Math.log(baseFriction) / FRAMERATE_MULTIPLIER)); } static void setPrefs(Map<String, Integer> prefs) { - FRICTION_SLOW = getFloatPref(prefs, PREF_SCROLLING_FRICTION_SLOW, 850); - FRICTION_FAST = getFloatPref(prefs, PREF_SCROLLING_FRICTION_FAST, 970); - VELOCITY_THRESHOLD = getIntPref(prefs, PREF_SCROLLING_VELOCITY_THRESHOLD, 10); + FRICTION_SLOW = getFrameAdjustedFriction(getFloatPref(prefs, PREF_SCROLLING_FRICTION_SLOW, 850)); + FRICTION_FAST = getFrameAdjustedFriction(getFloatPref(prefs, PREF_SCROLLING_FRICTION_FAST, 970)); + VELOCITY_THRESHOLD = 10 / FRAMERATE_MULTIPLIER; MAX_EVENT_ACCELERATION = getFloatPref(prefs, PREF_SCROLLING_MAX_EVENT_ACCELERATION, 12); - OVERSCROLL_DECEL_RATE = getFloatPref(prefs, PREF_SCROLLING_OVERSCROLL_DECEL_RATE, 40); + OVERSCROLL_DECEL_RATE = getFrameAdjustedFriction(getFloatPref(prefs, PREF_SCROLLING_OVERSCROLL_DECEL_RATE, 40)); SNAP_LIMIT = getFloatPref(prefs, PREF_SCROLLING_OVERSCROLL_SNAP_LIMIT, 300); MIN_SCROLLABLE_DISTANCE = getFloatPref(prefs, PREF_SCROLLING_MIN_SCROLLABLE_DISTANCE, 500); Log.i(LOGTAG, "Prefs: " + FRICTION_SLOW + "," + FRICTION_FAST + "," + VELOCITY_THRESHOLD + "," @@ -86,9 +85,6 @@ abstract class Axis { setPrefs(null); } - // The number of milliseconds per frame assuming 60 fps - private static final float MS_PER_FRAME = 1000.0f / 60.0f; - private enum FlingStates { STOPPED, PANNING, @@ -104,6 +100,7 @@ abstract class Axis { private final SubdocumentScrollHelper mSubscroller; + private int mOverscrollMode; /* Default to only overscrolling if we're allowed to scroll in a direction */ private float mFirstTouchPos; /* Position of the first touch event on the current drag. */ private float mTouchPos; /* Position of the most recent touch event on the current drag. */ private float mLastTouchPos; /* Position of the touch event before touchPos. */ @@ -121,6 +118,15 @@ abstract class Axis { Axis(SubdocumentScrollHelper subscroller) { mSubscroller = subscroller; + mOverscrollMode = View.OVER_SCROLL_IF_CONTENT_SCROLLS; + } + + public void setOverScrollMode(int overscrollMode) { + mOverscrollMode = overscrollMode; + } + + public int getOverScrollMode() { + return mOverscrollMode; } private float getViewportEnd() { @@ -155,7 +161,7 @@ abstract class Axis { // If there's a direction change, or current velocity is very low, // allow setting of the velocity outright. Otherwise, use the current // velocity and a maximum change factor to set the new velocity. - boolean curVelocityIsLow = Math.abs(mVelocity) < 1.0f; + boolean curVelocityIsLow = Math.abs(mVelocity) < 1.0f / FRAMERATE_MULTIPLIER; boolean directionChange = (mVelocity > 0) != (newVelocity > 0); if (curVelocityIsLow || (directionChange && !FloatUtils.fuzzyEquals(newVelocity, 0.0f))) { mVelocity = newVelocity; @@ -200,24 +206,31 @@ abstract class Axis { * Returns true if the page is zoomed in to some degree along this axis such that scrolling is * possible and this axis has not been scroll locked while panning. Otherwise, returns false. */ - private boolean scrollable() { + boolean scrollable() { // If we're scrolling a subdocument, ignore the viewport length restrictions (since those // apply to the top-level document) and only take into account axis locking. if (mSubscroller.scrolling()) { return !mScrollingDisabled; - } else { - return getViewportLength() <= getPageLength() - MIN_SCROLLABLE_DISTANCE && - !mScrollingDisabled; } + + // if we are axis locked, return false + if (mScrollingDisabled) { + return false; + } + + // there is scrollable space, and we're not disabled, or the document fits the viewport + // but we always allow overscroll anyway + return getViewportLength() <= getPageLength() - MIN_SCROLLABLE_DISTANCE || + getOverScrollMode() == View.OVER_SCROLL_ALWAYS; } /* * Returns the resistance, as a multiplier, that should be taken into account when * tracking or pinching. */ - float getEdgeResistance() { + float getEdgeResistance(boolean forPinching) { float excess = getExcess(); - if (excess > 0.0f) { + if (excess > 0.0f && (getOverscroll() == Overscroll.BOTH || !forPinching)) { // excess can be greater than viewport length, but the resistance // must never drop below 0.0 return Math.max(0.0f, SNAP_LIMIT - excess / getViewportLength()); @@ -257,7 +270,15 @@ abstract class Axis { } float excess = getExcess(); - if (mDisableSnap || FloatUtils.fuzzyEquals(excess, 0.0f)) { + Overscroll overscroll = getOverscroll(); + boolean decreasingOverscroll = false; + if ((overscroll == Overscroll.MINUS && mVelocity > 0) || + (overscroll == Overscroll.PLUS && mVelocity < 0)) + { + decreasingOverscroll = true; + } + + if (mDisableSnap || FloatUtils.fuzzyEquals(excess, 0.0f) || decreasingOverscroll) { // If we aren't overscrolled, just apply friction. if (Math.abs(mVelocity) >= VELOCITY_THRESHOLD) { mVelocity *= FRICTION_FAST; @@ -268,7 +289,7 @@ abstract class Axis { } else { // Otherwise, decrease the velocity linearly. float elasticity = 1.0f - excess / (getViewportLength() * SNAP_LIMIT); - if (getOverscroll() == Overscroll.MINUS) { + if (overscroll == Overscroll.MINUS) { mVelocity = Math.min((mVelocity + OVERSCROLL_DECEL_RATE) * elasticity, 0.0f); } else { // must be Overscroll.PLUS mVelocity = Math.max((mVelocity - OVERSCROLL_DECEL_RATE) * elasticity, 0.0f); @@ -285,14 +306,27 @@ abstract class Axis { // Performs displacement of the viewport position according to the current velocity. void displace() { - if (!scrollable()) { + // if this isn't scrollable just return + if (!scrollable()) return; - } if (mFlingState == FlingStates.PANNING) - mDisplacement += (mLastTouchPos - mTouchPos) * getEdgeResistance(); + mDisplacement += (mLastTouchPos - mTouchPos) * getEdgeResistance(false); else mDisplacement += mVelocity; + + // if overscroll is disabled and we're trying to overscroll, reset the displacement + // to remove any excess. Using getExcess alone isn't enough here since it relies on + // getOverscroll which doesn't take into account any new displacment being applied + if (getOverScrollMode() == View.OVER_SCROLL_NEVER) { + if (mDisplacement + getOrigin() < getPageStart()) { + mDisplacement = getPageStart() - getOrigin(); + stopFling(); + } else if (mDisplacement + getViewportEnd() > getPageEnd()) { + mDisplacement = getPageEnd() - getViewportEnd(); + stopFling(); + } + } } float resetDisplacement() { 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 a6b7d27..02e45da 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 @@ -19,8 +19,6 @@ import org.mozilla.gecko.gfx.ImmutableViewportMetrics; 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; @@ -53,26 +51,6 @@ public class PanZoomController // 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 float[] ZOOM_ANIMATION_FRAMES = new float[] { - 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 enum PanZoomState { NOTHING, /* no touch-start events received */ FLING, /* all touches removed, but we're still scrolling page */ @@ -125,6 +103,13 @@ public class PanZoomController mSubscroller.destroy(); } + private final static float easeOut(float t) { + // ease-out approx. + // -(t-1)^2+1 + t = t-1; + return -t*t+1; + } + private void setState(PanZoomState state) { mState = state; } @@ -145,23 +130,6 @@ 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); @@ -357,6 +325,8 @@ public class PanZoomController } private boolean onTouchCancel(MotionEvent event) { + cancelTouch(); + 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 @@ -367,7 +337,6 @@ public class PanZoomController return false; } - cancelTouch(); // ensure we snap back if we're overscrolled bounce(); return false; @@ -392,7 +361,9 @@ public class PanZoomController mY.startTouch(y); mLastEventTime = time; - if (angle < AXIS_LOCK_ANGLE || angle > (Math.PI - AXIS_LOCK_ANGLE)) { + if (!mX.scrollable() || !mY.scrollable()) { + setState(PanZoomState.PANNING); + } else if (angle < AXIS_LOCK_ANGLE || angle > (Math.PI - AXIS_LOCK_ANGLE)) { mY.setScrollingDisabled(true); setState(PanZoomState.PANNING_LOCKED); } else if (Math.abs(angle - (Math.PI / 2)) < AXIS_LOCK_ANGLE) { @@ -507,7 +478,7 @@ public class PanZoomController mAnimationTimer.scheduleAtFixedRate(new TimerTask() { @Override public void run() { mTarget.post(runnable); } - }, 0, 1000L/60L); + }, 0, (int)Axis.MS_PER_FRAME); } /* Stops the fling or bounce animation. */ @@ -607,7 +578,7 @@ public class PanZoomController } /* Perform the next frame of the bounce-back animation. */ - if (mBounceFrame < ZOOM_ANIMATION_FRAMES.length) { + if (mBounceFrame < (int)(256f/Axis.MS_PER_FRAME)) { advanceBounce(); return; } @@ -621,7 +592,7 @@ public class PanZoomController /* Performs one frame of a bounce animation. */ private void advanceBounce() { synchronized (mTarget.getLock()) { - float t = ZOOM_ANIMATION_FRAMES[mBounceFrame]; + float t = easeOut(mBounceFrame * Axis.MS_PER_FRAME / 256f); ViewportMetrics newMetrics = mBounceStartMetrics.interpolate(mBounceEndMetrics, t); mTarget.setViewportMetrics(newMetrics); mBounceFrame++; @@ -818,7 +789,7 @@ public class PanZoomController * Apply edge resistance if we're zoomed out smaller than the page size by scaling the zoom * factor toward 1.0. */ - float resistance = Math.min(mX.getEdgeResistance(), mY.getEdgeResistance()); + float resistance = Math.min(mX.getEdgeResistance(true), mY.getEdgeResistance(true)); if (spanRatio > 1.0f) spanRatio = 1.0f + (spanRatio - 1.0f) * resistance; else @@ -978,4 +949,13 @@ public class PanZoomController checkMainThread(); bounce(); } + + public void setOverScrollMode(int overscrollMode) { + mX.setOverScrollMode(overscrollMode); + mY.setOverScrollMode(overscrollMode); + } + + public int getOverScrollMode() { + return mX.getOverScrollMode(); + } } commit 590fdf41164347f113cccc01953eefe6ef1020a8 Author: Tomaž Vajngerl <tomaz.vajng...@collabora.com> Date: Sat Sep 27 22:30:57 2014 +0200 android: solve black screen on load - call setForceRedraw Change-Id: Ia9ae05a14c0c751fde961186be350d57d5308519 diff --git a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java index 349de7b..ddc18d4 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.DisplayMetrics; import android.util.Log; import org.mozilla.gecko.gfx.GeckoLayerClient; +import org.mozilla.gecko.gfx.LayerController; import org.mozilla.gecko.gfx.ViewportMetrics; import java.util.concurrent.LinkedBlockingQueue; @@ -19,6 +20,8 @@ public class LOKitThread extends Thread { private TileProvider mTileProvider; private ViewportMetrics mViewportMetrics; private boolean mCheckboardImageSet = false; + private GeckoLayerClient mLayerClient; + private LayerController mController; public LOKitThread() { TileProviderFactory.initialize(); @@ -55,16 +58,21 @@ public class LOKitThread extends Thread { if (mApplication == null) { mApplication = LibreOfficeMainActivity.mAppContext; } + + mController = mApplication.getLayerController(); + mLayerClient = mApplication.getLayerClient(); + if (mTileProvider != null) { mTileProvider.close(); } - GeckoLayerClient layerClient = mApplication.getLayerClient(); - mTileProvider = TileProviderFactory.create(mApplication.getLayerController(), filename); - layerClient.setTileProvider(mTileProvider); + + mTileProvider = TileProviderFactory.create(mController, filename); + mLayerClient.setTileProvider(mTileProvider); boolean isReady = mTileProvider.isReady(); if (isReady) { updateCheckbardImage(); + mController.setForceRedraw(); } return isReady; } commit 40e919d2a19066bd7bcbbd4ece2eae75a1fd20a5 Author: Tomaž Vajngerl <tomaz.vajng...@collabora.com> Date: Sat Sep 27 12:54:47 2014 +0200 android: remove notifyLayerClientOfGeometryChange (Fennec import) Change-Id: Ibc1f4d11dcfdf177cd45fcf689b518d975b13709 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 0898397..ca02e1c 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 @@ -87,6 +87,7 @@ public class LayerController implements PanZoomTarget { public void setForceRedraw() { mForceRedraw = true; + notifyLayerClientOfGeometryChange(); } public Layer getRoot() { return mRootLayer; } @@ -159,14 +160,13 @@ public class LayerController implements PanZoomTarget { } /** - * 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. + * Sets the entire viewport metrics at once. + * You must hold the monitor while calling this. */ public void setViewportMetrics(ViewportMetrics viewport) { mViewportMetrics = new ImmutableViewportMetrics(viewport); mView.requestRender(); + notifyLayerClientOfGeometryChange(); } public void setAnimationTarget(ViewportMetrics viewport) { @@ -183,11 +183,7 @@ public class LayerController implements PanZoomTarget { public boolean post(Runnable action) { return mView.post(action); } - /** - * The view as well as the controller itself use this method to notify the layer client that - * the geometry changed. - */ - public void notifyLayerClientOfGeometryChange() { + private void notifyLayerClientOfGeometryChange() { if (mLayerClient != null) mLayerClient.geometryChanged(); } 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 876f626..a6b7d27 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 @@ -195,7 +195,6 @@ public class PanZoomController // transitions. synchronized (mTarget.getLock()) { mTarget.setViewportMetrics(getValidViewportMetrics()); - mTarget.notifyLayerClientOfGeometryChange(); } break; } @@ -233,7 +232,6 @@ public class PanZoomController // page size changed such that we are now in overscroll. snap to the // the nearest valid viewport mTarget.setViewportMetrics(validated); - mTarget.notifyLayerClientOfGeometryChange(); } } } @@ -254,7 +252,6 @@ public class PanZoomController // case this touchstart is just a tap that doesn't end up triggering // a redraw mTarget.setForceRedraw(); - mTarget.notifyLayerClientOfGeometryChange(); // fall through case FLING: case BOUNCE: @@ -460,7 +457,6 @@ public class PanZoomController viewportMetrics.setOrigin(origin); mTarget.setViewportMetrics(viewportMetrics); - mTarget.notifyLayerClientOfGeometryChange(); } private void fling() { @@ -628,7 +624,6 @@ public class PanZoomController float t = ZOOM_ANIMATION_FRAMES[mBounceFrame]; ViewportMetrics newMetrics = mBounceStartMetrics.interpolate(mBounceEndMetrics, t); mTarget.setViewportMetrics(newMetrics); - mTarget.notifyLayerClientOfGeometryChange(); mBounceFrame++; } } @@ -637,7 +632,6 @@ public class PanZoomController private void finishBounce() { synchronized (mTarget.getLock()) { mTarget.setViewportMetrics(mBounceEndMetrics); - mTarget.notifyLayerClientOfGeometryChange(); mBounceFrame = -1; } } @@ -699,7 +693,6 @@ public class PanZoomController // Force a viewport synchronisation mTarget.setForceRedraw(); - mTarget.notifyLayerClientOfGeometryChange(); } /* Returns the nearest viewport metrics with no overscroll visible. */ @@ -883,7 +876,6 @@ public class PanZoomController // Force a viewport synchronisation mTarget.setForceRedraw(); - mTarget.notifyLayerClientOfGeometryChange(); } /** @@ -894,7 +886,6 @@ public class PanZoomController ViewportMetrics viewportMetrics = getMutableMetrics(); viewportMetrics.scaleTo(zoomFactor, focus); mTarget.setViewportMetrics(viewportMetrics); - mTarget.notifyLayerClientOfGeometryChange(); } public boolean getRedrawHint() { diff --git a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/ui/PanZoomTarget.java b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/ui/PanZoomTarget.java index fcbc00f..3ebc4f1 100644 --- a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/ui/PanZoomTarget.java +++ b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/ui/PanZoomTarget.java @@ -17,8 +17,6 @@ public interface PanZoomTarget { public void setAnimationTarget(ViewportMetrics viewport); public void setViewportMetrics(ViewportMetrics viewport); - - public void notifyLayerClientOfGeometryChange(); public void setForceRedraw(); public boolean post(Runnable action); commit 0151bf3f28b476a2fd0993c6a60fbf9a9f555c3a Author: Tomaž Vajngerl <tomaz.vajng...@collabora.com> Date: Sat Sep 27 12:47:47 2014 +0200 android: move scrollBy & scaleWithFocus to PZC (Fennec Import) Change-Id: Ie0d23b302994134f9d382e255b0408f7c9f1c1fb 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 db43db2..0898397 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 @@ -135,18 +135,6 @@ public class LayerController implements PanZoomTarget { } } - /** 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 ImmutableViewportMetrics(viewportMetrics); - - notifyLayerClientOfGeometryChange(); - mView.requestRender(); - } - /** Sets the current page rect. You must hold the monitor while calling this. */ public void setPageRect(RectF rect, RectF cssRect) { // Since the "rect" is always just a multiple of "cssRect" we don't need to @@ -193,21 +181,6 @@ public class LayerController implements PanZoomTarget { } } - /** - * Scales the viewport, keeping the given focus point in the same place before and after the - * scale operation. You must hold the monitor while calling this. - */ - public void scaleWithFocus(float zoomFactor, PointF focus) { - ViewportMetrics viewportMetrics = new ViewportMetrics(mViewportMetrics); - viewportMetrics.scaleTo(zoomFactor, focus); - mViewportMetrics = new ImmutableViewportMetrics(viewportMetrics); - - // We assume the zoom level will only be modified by the - // PanZoomController, so no need to notify it of this change. - notifyLayerClientOfGeometryChange(); - mView.requestRender(); - } - public boolean post(Runnable action) { return mView.post(action); } /** 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 ef45fd3..876f626 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 @@ -133,6 +133,10 @@ public class PanZoomController return mTarget.getViewportMetrics(); } + private ViewportMetrics getMutableMetrics() { + return new ViewportMetrics(getMetrics()); + } + // for debugging bug 713011; it can be taken out once that is resolved. private void checkMainThread() { if (mMainThread != Thread.currentThread()) { @@ -225,7 +229,7 @@ public class PanZoomController if (mState == PanZoomState.NOTHING) { synchronized (mTarget.getLock()) { ViewportMetrics validated = getValidViewportMetrics(); - if (! (new ViewportMetrics(getMetrics())).fuzzyEquals(validated)) { + if (! getMutableMetrics().fuzzyEquals(validated)) { // page size changed such that we are now in overscroll. snap to the // the nearest valid viewport mTarget.setViewportMetrics(validated); @@ -449,6 +453,16 @@ public class PanZoomController updatePosition(); } + private void scrollBy(PointF point) { + ViewportMetrics viewportMetrics = getMutableMetrics(); + PointF origin = viewportMetrics.getOrigin(); + origin.offset(point.x, point.y); + viewportMetrics.setOrigin(origin); + + mTarget.setViewportMetrics(viewportMetrics); + mTarget.notifyLayerClientOfGeometryChange(); + } + private void fling() { updatePosition(); @@ -465,7 +479,7 @@ public class PanZoomController private void bounce(ViewportMetrics metrics) { stopAnimationTimer(); - ViewportMetrics bounceStartMetrics = new ViewportMetrics(getMetrics()); + ViewportMetrics bounceStartMetrics = getMutableMetrics(); if (bounceStartMetrics.fuzzyEquals(metrics)) { setState(PanZoomState.NOTHING); return; @@ -539,7 +553,7 @@ public class PanZoomController } if (! mSubscroller.scrollBy(displacement)) { synchronized (mTarget.getLock()) { - mTarget.scrollBy(displacement); + scrollBy(displacement); } } } @@ -690,7 +704,7 @@ public class PanZoomController /* Returns the nearest viewport metrics with no overscroll visible. */ private ViewportMetrics getValidViewportMetrics() { - return getValidViewportMetrics(new ViewportMetrics(getMetrics())); + return getValidViewportMetrics(getMutableMetrics()); } private ViewportMetrics getValidViewportMetrics(ViewportMetrics viewportMetrics) { @@ -848,10 +862,10 @@ public class PanZoomController newZoomFactor = maxZoomFactor + excessZoom; } - mTarget.scrollBy(new PointF(mLastZoomFocus.x - detector.getFocusX(), + scrollBy(new PointF(mLastZoomFocus.x - detector.getFocusX(), mLastZoomFocus.y - detector.getFocusY())); PointF focus = new PointF(detector.getFocusX(), detector.getFocusY()); - mTarget.scaleWithFocus(newZoomFactor, focus); + scaleWithFocus(newZoomFactor, focus); } mLastZoomFocus.set(detector.getFocusX(), detector.getFocusY()); @@ -872,6 +886,17 @@ public class PanZoomController mTarget.notifyLayerClientOfGeometryChange(); } + /** + * Scales the viewport, keeping the given focus point in the same place before and after the + * scale operation. You must hold the monitor while calling this. + */ + private void scaleWithFocus(float zoomFactor, PointF focus) { + ViewportMetrics viewportMetrics = getMutableMetrics(); + viewportMetrics.scaleTo(zoomFactor, focus); + mTarget.setViewportMetrics(viewportMetrics); + mTarget.notifyLayerClientOfGeometryChange(); + } + public boolean getRedrawHint() { switch (mState) { case PINCHING: @@ -944,7 +969,7 @@ public class PanZoomController float finalZoom = viewport.width() / zoomToRect.width(); - ViewportMetrics finalMetrics = new ViewportMetrics(getMetrics()); + ViewportMetrics finalMetrics = getMutableMetrics(); finalMetrics.setOrigin(new PointF(zoomToRect.left * finalMetrics.getZoomFactor(), zoomToRect.top * finalMetrics.getZoomFactor())); finalMetrics.scaleTo(finalZoom, new PointF(0.0f, 0.0f)); diff --git a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/ui/PanZoomTarget.java b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/ui/PanZoomTarget.java index 7ab1c56..fcbc00f 100644 --- a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/ui/PanZoomTarget.java +++ b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/ui/PanZoomTarget.java @@ -17,8 +17,6 @@ public interface PanZoomTarget { public void setAnimationTarget(ViewportMetrics viewport); public void setViewportMetrics(ViewportMetrics viewport); - public void scrollBy(PointF point); - public void scaleWithFocus(float zoomFactor, PointF focus); public void notifyLayerClientOfGeometryChange(); public void setForceRedraw(); commit 94e7296fc51e1e85715bbab91cf233f3d7ce67f3 Author: Tomaž Vajngerl <tomaz.vajng...@collabora.com> Date: Sat Sep 27 12:42:39 2014 +0200 android: add PanZoomTarget (Fennec import) Change-Id: Ib946b7a95cd59833a732e3da6c139e848778e1bc 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 303cb67..db43db2 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 @@ -17,6 +17,7 @@ import android.view.GestureDetector; import org.mozilla.gecko.ZoomConstraints; import org.mozilla.gecko.ui.PanZoomController; +import org.mozilla.gecko.ui.PanZoomTarget; import org.mozilla.gecko.ui.SimpleScaleGestureDetector; /** @@ -26,7 +27,7 @@ import org.mozilla.gecko.ui.SimpleScaleGestureDetector; * * Many methods require that the monitor be held, with a synchronized (controller) { ... } block. */ -public class LayerController { +public class LayerController implements PanZoomTarget { private static final String LOGTAG = "GeckoLayerController"; private Layer mRootLayer; /* The root layer. */ @@ -92,6 +93,7 @@ public class LayerController { public LayerView getView() { return mView; } public Context getContext() { return mContext; } public ImmutableViewportMetrics getViewportMetrics() { return mViewportMetrics; } + public Object getLock() { return this; } public FloatSize getViewportSize() { return mViewportMetrics.getSize(); 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 1d7d52c..ef45fd3 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 @@ -16,7 +16,6 @@ import org.libreoffice.LOKitShell; import org.libreoffice.LibreOfficeMainActivity; import org.mozilla.gecko.ZoomConstraints; import org.mozilla.gecko.gfx.ImmutableViewportMetrics; -import org.mozilla.gecko.gfx.LayerController; import org.mozilla.gecko.gfx.ViewportMetrics; import org.mozilla.gecko.util.FloatUtils; @@ -92,7 +91,7 @@ public class PanZoomController prevented the default actions yet. we still need to abort animations. */ } - private final LayerController mController; + private final PanZoomTarget mTarget; private final SubdocumentScrollHelper mSubscroller; private final Axis mX; private final Axis mY; @@ -110,8 +109,8 @@ public class PanZoomController /* Current state the pan/zoom UI is in. */ private PanZoomState mState; - public PanZoomController(LayerController controller) { - mController = controller; + public PanZoomController(PanZoomTarget target) { + mTarget = target; mSubscroller = new SubdocumentScrollHelper(); mX = new AxisX(mSubscroller); mY = new AxisY(mSubscroller); @@ -131,7 +130,7 @@ public class PanZoomController } private ImmutableViewportMetrics getMetrics() { - return mController.getViewportMetrics(); + return mTarget.getViewportMetrics(); } // for debugging bug 713011; it can be taken out once that is resolved. @@ -190,9 +189,9 @@ public class PanZoomController case NOTHING: // Don't do animations here; they're distracting and can cause flashes on page // transitions. - synchronized (mController) { - mController.setViewportMetrics(getValidViewportMetrics()); - mController.notifyLayerClientOfGeometryChange(); + synchronized (mTarget.getLock()) { + mTarget.setViewportMetrics(getValidViewportMetrics()); + mTarget.notifyLayerClientOfGeometryChange(); } break; } @@ -224,13 +223,13 @@ public class PanZoomController /** This must be called on the UI thread. */ public void pageRectUpdated() { if (mState == PanZoomState.NOTHING) { - synchronized (mController) { + synchronized (mTarget.getLock()) { ViewportMetrics validated = getValidViewportMetrics(); if (! (new ViewportMetrics(getMetrics())).fuzzyEquals(validated)) { // page size changed such that we are now in overscroll. snap to the // the nearest valid viewport - mController.setViewportMetrics(validated); - mController.notifyLayerClientOfGeometryChange(); + mTarget.setViewportMetrics(validated); + mTarget.notifyLayerClientOfGeometryChange(); } } } @@ -250,8 +249,8 @@ public class PanZoomController // 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(); + mTarget.setForceRedraw(); + mTarget.notifyLayerClientOfGeometryChange(); // fall through case FLING: case BOUNCE: @@ -476,7 +475,7 @@ public class PanZoomController // 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); + mTarget.setAnimationTarget(metrics); startAnimationTimer(new BounceRunnable(bounceStartMetrics, metrics)); } @@ -497,7 +496,7 @@ public class PanZoomController mAnimationRunnable = runnable; mAnimationTimer.scheduleAtFixedRate(new TimerTask() { @Override - public void run() { mController.post(runnable); } + public void run() { mTarget.post(runnable); } }, 0, 1000L/60L); } @@ -539,8 +538,8 @@ public class PanZoomController return; } if (! mSubscroller.scrollBy(displacement)) { - synchronized (mController) { - mController.scrollBy(displacement); + synchronized (mTarget.getLock()) { + mTarget.scrollBy(displacement); } } } @@ -611,20 +610,20 @@ public class PanZoomController /* Performs one frame of a bounce animation. */ private void advanceBounce() { - synchronized (mController) { + synchronized (mTarget.getLock()) { float t = ZOOM_ANIMATION_FRAMES[mBounceFrame]; ViewportMetrics newMetrics = mBounceStartMetrics.interpolate(mBounceEndMetrics, t); ... etc. - the rest is truncated
_______________________________________________ Libreoffice-commits mailing list libreoffice-comm...@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/libreoffice-commits