[Libreoffice-commits] online.git: loleaflet/src
Dennis Francis (via logerrit)
logerrit at kemper.freedesktop.org
Wed Aug 12 08:31:39 UTC 2020
loleaflet/src/geometry/Bounds.js | 39 ++
loleaflet/src/layer/SplitPanesContext.js | 37 ++
loleaflet/src/layer/tile/CanvasTileLayer.js | 458 ++++++++++++++++++--------
loleaflet/src/layer/tile/GridLayer.js | 23 +
loleaflet/src/map/handler/Map.TouchGesture.js | 7
5 files changed, 427 insertions(+), 137 deletions(-)
New commits:
commit 06e4722cc93a4acdbf47700de97ea1a8e98ad6fa
Author: Dennis Francis <dennis.francis at collabora.com>
AuthorDate: Fri Aug 7 18:37:11 2020 +0530
Commit: Dennis Francis <dennis.francis at collabora.com>
CommitDate: Wed Aug 12 10:31:19 2020 +0200
loleaflet: rewrite tile-prefetcher for L.CanvasTileLayer...
with full support and optimizations for split-panes. This includes some
refactors, for instance the prefetcher implementation is taken out of
the doc-layer methods as a L.TilesPrefetcher class.
Note: This only affects Calc as Writer/Impress are still using
L.TileLayer.
Change-Id: I0e43b58bad0a97d44cbffd1ee0d90d94a9426e29
Reviewed-on: https://gerrit.libreoffice.org/c/online/+/100485
Tested-by: Jenkins
Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoffice at gmail.com>
Reviewed-by: Dennis Francis <dennis.francis at collabora.com>
diff --git a/loleaflet/src/geometry/Bounds.js b/loleaflet/src/geometry/Bounds.js
index cadc8e225..8dacdc832 100644
--- a/loleaflet/src/geometry/Bounds.js
+++ b/loleaflet/src/geometry/Bounds.js
@@ -175,7 +175,44 @@ L.Bounds.prototype = {
}
return false;
- }
+ },
+
+ clampX: function (x) {
+ return Math.max(this.min.x, Math.min(this.max.x, x));
+ },
+
+ clampY: function (y) {
+ return Math.max(this.min.y, Math.min(this.max.y, y));
+ },
+
+ clamp: function (obj) { // (Point) -> Point or (Bounds) -> Bounds
+ if (obj instanceof L.Point) {
+ return new L.Point(
+ this.clampX(obj.x),
+ this.clampY(obj.y)
+ );
+ }
+
+ if (obj instanceof L.Bounds) {
+ return new L.Bounds(
+ new L.Point(
+ this.clampX(obj.min.x),
+ this.clampY(obj.min.y)
+ ),
+
+ new L.Point(
+ this.clampX(obj.max.x),
+ this.clampY(obj.max.y)
+ )
+ );
+ }
+
+ console.error('invalid argument type');
+ },
+
+ equals: function (bounds) { // (Bounds) -> Boolean
+ return this.min.equals(bounds.min) && this.max.equals(bounds.max);
+ },
};
L.bounds = function (a, b) { // (Bounds) or (Point, Point) or (Point[])
diff --git a/loleaflet/src/layer/SplitPanesContext.js b/loleaflet/src/layer/SplitPanesContext.js
index 01920ec8e..b75272221 100644
--- a/loleaflet/src/layer/SplitPanesContext.js
+++ b/loleaflet/src/layer/SplitPanesContext.js
@@ -158,6 +158,41 @@ L.SplitPanesContext = L.Class.extend({
this._docLayer.updateVertPaneSplitter();
},
+ getPanesProperties: function () {
+ var paneStatusList = [];
+ if (this._splitPos.x && this._splitPos.y) {
+ // top-left pane
+ paneStatusList.push({
+ xFixed: true,
+ yFixed: true,
+ });
+ }
+
+ if (this._splitPos.y) {
+ // top-right pane or top half pane
+ paneStatusList.push({
+ xFixed: false,
+ yFixed: true,
+ });
+ }
+
+ if (this._splitPos.x) {
+ // bottom-left pane or left half pane
+ paneStatusList.push({
+ xFixed: true,
+ yFixed: false,
+ });
+ }
+
+ // bottom-right/bottom-half/right-half pane or the full pane (when there are no split-panes active)
+ paneStatusList.push({
+ xFixed: false,
+ yFixed: false,
+ });
+
+ return paneStatusList;
+ },
+
// returns all the pane rectangles for the provided full-map area (all in CSS pixels).
getPxBoundList: function (pxBounds) {
if (!pxBounds) {
@@ -191,7 +226,7 @@ L.SplitPanesContext = L.Class.extend({
));
}
- // bottom-right pane or the full pane (when there are no split-panes active)
+ // bottom-right/bottom-half/right-half pane or the full pane (when there are no split-panes active)
boundList.push(new L.Bounds(
topLeft.add(this._splitPos).add(new L.Point(1, 1)),
bottomRight
diff --git a/loleaflet/src/layer/tile/CanvasTileLayer.js b/loleaflet/src/layer/tile/CanvasTileLayer.js
index 3e86d3f0f..4d7d5dc1f 100644
--- a/loleaflet/src/layer/tile/CanvasTileLayer.js
+++ b/loleaflet/src/layer/tile/CanvasTileLayer.js
@@ -1160,141 +1160,30 @@ L.CanvasTileLayer = L.TileLayer.extend({
});
},
- _preFetchTiles: function () {
- if (this._emptyTilesCount > 0 || !this._map) {
- return;
- }
- var center = this._map.getCenter();
- var zoom = this._map.getZoom();
- var tilesToFetch = 10;
- var maxBorderWidth = 5;
- var tileBorderSrcs;
-
- if (this._map.isPermissionEdit()) {
- tilesToFetch = 5;
- maxBorderWidth = 3;
+ _preFetchTiles: function (forceBorderCalc) {
+ if (this._prefetcher) {
+ this._prefetcher.preFetchTiles(forceBorderCalc);
}
+ },
- if (!this._preFetchBorders) {
- var pixelBounds = this._map.getPixelBounds(center, zoom);
- tileBorderSrcs = this._pxBoundsToTileRanges(pixelBounds);
- this._preFetchBorders = tileBorderSrcs;
- }
- else {
- tileBorderSrcs = this._preFetchBorders;
+ _resetPreFetching: function (resetBorder) {
+ if (!this._prefetcher) {
+ this._prefetcher = new L.TilesPreFetcher(this, this._map);
}
- var queue = [];
- var finalQueue = [];
- var visitedTiles = {};
- var borderWidth = 0;
- // don't search on a border wider than 5 tiles because it will freeze the UI
-
- for (var rangeIdx = 0; rangeIdx < tileBorderSrcs.length; ++rangeIdx) {
- var tileBorder = new L.Bounds(
- tileBorderSrcs[rangeIdx].min,
- tileBorderSrcs[rangeIdx].max
- );
-
- while ((tileBorder.min.x >= 0 || tileBorder.min.y >= 0 ||
- tileBorder.max.x * this._tileWidthTwips < this._docWidthTwips ||
- tileBorder.max.y * this._tileHeightTwips < this._docHeightTwips) &&
- tilesToFetch > 0 && borderWidth < maxBorderWidth) {
- // while the bounds do not fully contain the document
-
- for (var i = tileBorder.min.x; i <= tileBorder.max.x; i++) {
- // tiles below the visible area
- var coords = new L.TileCoordData(
- i * this._tileSize,
- tileBorder.max.y * this._tileSize);
- queue.push(coords);
- }
- for (i = tileBorder.min.x; i <= tileBorder.max.x; i++) {
- // tiles above the visible area
- coords = new L.TileCoordData(
- i * this._tileSize,
- tileBorder.min.y * this._tileSize);
- queue.push(coords);
- }
- for (i = tileBorder.min.y; i <= tileBorder.max.y; i++) {
- // tiles to the right of the visible area
- coords = new L.TileCoordData(
- tileBorder.max.x * this._tileSize,
- i * this._tileSize);
- queue.push(coords);
- }
- for (i = tileBorder.min.y; i <= tileBorder.max.y; i++) {
- // tiles to the left of the visible area
- coords = new L.TileCoordData(
- tileBorder.min.x * this._tileSize,
- i * this._tileSize);
- queue.push(coords);
- }
-
- for (i = 0; i < queue.length && tilesToFetch > 0; i++) {
- coords = queue[i];
- coords.z = zoom;
- coords.part = this._preFetchPart;
- var key = this._tileCoordsToKey(coords);
-
- if (!this._isValidTile(coords) ||
- this._tiles[key] ||
- this._tileCache[key] ||
- visitedTiles[key]) {
- continue;
- }
-
- visitedTiles[key] = true;
- finalQueue.push(coords);
- tilesToFetch -= 1;
- }
- if (tilesToFetch === 0) {
- // don't update the border as there are still
- // some tiles to be fetched
- continue;
- }
- if (tileBorder.min.x >= 0) {
- tileBorder.min.x -= 1;
- }
- if (tileBorder.min.y >= 0) {
- tileBorder.min.y -= 1;
- }
- if (tileBorder.max.x * this._tileWidthTwips <= this._docWidthTwips) {
- tileBorder.max.x += 1;
- }
- if (tileBorder.max.y * this._tileHeightTwips <= this._docHeightTwips) {
- tileBorder.max.y += 1;
- }
- borderWidth += 1;
- }
- }
+ this._prefetcher.resetPreFetching(resetBorder);
+ },
- if (finalQueue.length > 0) {
- this._addTiles(finalQueue);
- } else {
- clearInterval(this._tilesPreFetcher);
- this._tilesPreFetcher = undefined;
+ _clearPreFetch: function () {
+ if (this._prefetcher) {
+ this._prefetcher.clearPreFetch();
}
},
- _resetPreFetching: function (resetBorder) {
- if (!this._map) {
- return;
+ _clearTilesPreFetcher: function () {
+ if (this._prefetcher) {
+ this._prefetcher.clearTilesPreFetcher();
}
- if (this._tilesPreFetcher)
- clearInterval(this._tilesPreFetcher);
- if (this._preFetchIdle)
- clearTimeout(this._preFetchIdle);
- if (resetBorder) {
- this._preFetchBorders = null;
- }
- var interval = 750;
- var idleTime = 5000;
- this._preFetchPart = this._selectedPart;
- this._preFetchIdle = setTimeout(L.bind(function () {
- this._tilesPreFetcher = setInterval(L.bind(this._preFetchTiles, this), interval);
- this._prefetchIdle = undefined;
- }, this), idleTime);
},
_onTileMsg: function (textMsg, img) {
@@ -1407,3 +1296,320 @@ L.CanvasTileLayer = L.TileLayer.extend({
},
});
+
+L.TilesPreFetcher = L.Class.extend({
+
+ initialize: function (docLayer, map) {
+ this._docLayer = docLayer;
+ this._map = map;
+ },
+
+ preFetchTiles: function (forceBorderCalc) {
+
+ if (this._docLayer._emptyTilesCount > 0 || !this._map || !this._docLayer) {
+ return;
+ }
+
+ var center = this._map.getCenter();
+ var zoom = this._map.getZoom();
+ var part = this._docLayer._selectedPart;
+ var hasEditPerm = this._map.isPermissionEdit();
+
+ if (this._zoom === undefined) {
+ this._zoom = zoom;
+ }
+
+ if (this._preFetchPart === undefined) {
+ this._preFetchPart = part;
+ }
+
+ if (this._hasEditPerm === undefined) {
+ this._hasEditPerm = hasEditPerm;
+ }
+
+ var maxTilesToFetch = 10;
+ // don't search on a border wider than 5 tiles because it will freeze the UI
+ var maxBorderWidth = 5;
+
+ if (hasEditPerm) {
+ maxTilesToFetch = 5;
+ maxBorderWidth = 3;
+ }
+
+ var tileSize = this._docLayer._tileSize;
+ var pixelBounds = this._map.getPixelBounds(center, zoom);
+
+ if (this._pixelBounds === undefined) {
+ this._pixelBounds = pixelBounds;
+ }
+
+ var splitPanesContext = this._docLayer.getSplitPanesContext();
+ var splitPos = splitPanesContext ? splitPanesContext.getSplitPos() : new L.Point(0, 0);
+
+ if (this._splitPos === undefined) {
+ this._splitPos = splitPos;
+ }
+
+ var paneXFixed = false;
+ var paneYFixed = false;
+
+ if (forceBorderCalc ||
+ !this._borders || this._borders.length === 0 ||
+ zoom !== this._zoom ||
+ part !== this._preFetchPart ||
+ hasEditPerm !== this._hasEditPerm ||
+ !pixelBounds.equals(this._pixelBounds) ||
+ !splitPos.equals(this._splitPos)) {
+
+ this._zoom = zoom;
+ this._preFetchPart = part;
+ this._hasEditPerm = hasEditPerm;
+ this._pixelBounds = pixelBounds;
+ this._splitPos = splitPos;
+
+ // Need to compute borders afresh and fetch tiles for them.
+ this._borders = []; // Stores borders for each split-pane.
+ var tileRanges = this._docLayer._pxBoundsToTileRanges(pixelBounds);
+ var paneStatusList = splitPanesContext ? splitPanesContext.getPanesProperties() :
+ [ { xFixed: false, yFixed: false} ];
+
+ console.assert(tileRanges.length === paneStatusList.length, 'tileRanges and paneStatusList should agree on the number of split-panes');
+
+ for (var paneIdx = 0; paneIdx < tileRanges.length; ++paneIdx) {
+ paneXFixed = paneStatusList[paneIdx].xFixed;
+ paneYFixed = paneStatusList[paneIdx].yFixed;
+
+ if (paneXFixed && paneYFixed) {
+ continue;
+ }
+
+ var tileRange = tileRanges[paneIdx];
+ var paneBorder = new L.Bounds(
+ tileRange.min.add(new L.Point(-1, -1)),
+ tileRange.max.add(new L.Point(1, 1))
+ );
+
+ this._borders.push(new L.TilesPreFetcher.PaneBorder(paneBorder, paneXFixed, paneYFixed));
+ }
+
+ }
+
+ var finalQueue = [];
+ var visitedTiles = {};
+
+ var validTileRange = new L.Bounds(
+ new L.Point(0, 0),
+ new L.Point(
+ Math.floor((this._docLayer._docWidthTwips - 1) / this._docLayer._tileWidthTwips),
+ Math.floor((this._docLayer._docHeightTwips - 1) / this._docLayer._tileHeightTwips)
+ )
+ );
+
+ var tilesToFetch = maxTilesToFetch; // total tile limit per call of preFetchTiles()
+ var doneAllPanes = true;
+
+ for (paneIdx = 0; paneIdx < this._borders.length; ++paneIdx) {
+
+ var queue = [];
+ paneBorder = this._borders[paneIdx];
+ var borderBounds = paneBorder.getBorderBounds();
+
+ paneXFixed = paneBorder.isXFixed();
+ paneYFixed = paneBorder.isYFixed();
+
+ while (tilesToFetch > 0 && paneBorder.getBorderIndex() < maxBorderWidth) {
+
+ var clampedBorder = validTileRange.clamp(borderBounds);
+ var fetchTopBorder = !paneYFixed && borderBounds.min.y === clampedBorder.min.y;
+ var fetchBottomBorder = !paneYFixed && borderBounds.max.y === clampedBorder.max.y;
+ var fetchLeftBorder = !paneXFixed && borderBounds.min.x === clampedBorder.min.x;
+ var fetchRightBorder = !paneXFixed && borderBounds.max.x === clampedBorder.max.x;
+
+ if (!fetchLeftBorder && !fetchRightBorder && !fetchTopBorder && !fetchBottomBorder) {
+ break;
+ }
+
+ if (fetchBottomBorder) {
+ for (var i = clampedBorder.min.x; i <= clampedBorder.max.x; i++) {
+ // tiles below the visible area
+ var coords = new L.TileCoordData(
+ i * tileSize,
+ borderBounds.max.y * tileSize);
+ queue.push(coords);
+ }
+ }
+
+ if (fetchTopBorder) {
+ for (i = clampedBorder.min.x; i <= clampedBorder.max.x; i++) {
+ // tiles above the visible area
+ coords = new L.TileCoordData(
+ i * tileSize,
+ borderBounds.min.y * tileSize);
+ queue.push(coords);
+ }
+ }
+
+ if (fetchRightBorder) {
+ for (i = clampedBorder.min.y; i <= clampedBorder.max.y; i++) {
+ // tiles to the right of the visible area
+ coords = new L.TileCoordData(
+ borderBounds.max.x * tileSize,
+ i * tileSize);
+ queue.push(coords);
+ }
+ }
+
+ if (fetchLeftBorder) {
+ for (i = clampedBorder.min.y; i <= clampedBorder.max.y; i++) {
+ // tiles to the left of the visible area
+ coords = new L.TileCoordData(
+ borderBounds.min.x * tileSize,
+ i * tileSize);
+ queue.push(coords);
+ }
+ }
+
+ var tilesPending = false;
+ for (i = 0; i < queue.length; i++) {
+ coords = queue[i];
+ coords.z = zoom;
+ coords.part = this._preFetchPart;
+ var key = this._docLayer._tileCoordsToKey(coords);
+
+ if (!this._docLayer._isValidTile(coords) ||
+ this._docLayer._tiles[key] ||
+ this._docLayer._tileCache[key] ||
+ visitedTiles[key]) {
+ continue;
+ }
+
+ if (tilesToFetch > 0) {
+ visitedTiles[key] = true;
+ finalQueue.push(coords);
+ tilesToFetch -= 1;
+ }
+ else {
+ tilesPending = true;
+ }
+ }
+
+ if (tilesPending) {
+ // don't update the border as there are still
+ // some tiles to be fetched
+ continue;
+ }
+
+ if (!paneXFixed) {
+ if (borderBounds.min.x > 0) {
+ borderBounds.min.x -= 1;
+ }
+ if (borderBounds.max.x < validTileRange.max.x) {
+ borderBounds.max.x += 1;
+ }
+ }
+
+ if (!paneYFixed) {
+ if (borderBounds.min.y > 0) {
+ borderBounds.min.y -= 1;
+ }
+
+ if (borderBounds.max.y < validTileRange.max.y) {
+ borderBounds.max.y += 1;
+ }
+ }
+
+ paneBorder.incBorderIndex();
+
+ } // border width loop end
+
+ if (paneBorder.getBorderIndex() < maxBorderWidth) {
+ doneAllPanes = false;
+ }
+ } // pane loop end
+
+ console.assert(finalQueue.length <= maxTilesToFetch,
+ 'finalQueue length(' + finalQueue.length + ') exceeded maxTilesToFetch(' + maxTilesToFetch + ')');
+
+ var tilesRequested = false;
+
+ if (finalQueue.length > 0) {
+ this._cumTileCount += finalQueue.length;
+ this._docLayer._addTiles(finalQueue);
+ tilesRequested = true;
+ }
+
+ if (!tilesRequested || doneAllPanes) {
+ this.clearTilesPreFetcher();
+ this._borders = undefined;
+ }
+ },
+
+ resetPreFetching: function (resetBorder) {
+
+ if (!this._map) {
+ return;
+ }
+
+ this.clearPreFetch();
+
+ if (resetBorder) {
+ this._borders = undefined;
+ }
+
+ var interval = 750;
+ var idleTime = 5000;
+ this._preFetchPart = this._docLayer._selectedPart;
+ this._preFetchIdle = setTimeout(L.bind(function () {
+ this._tilesPreFetcher = setInterval(L.bind(this.preFetchTiles, this), interval);
+ this._preFetchIdle = undefined;
+ this._cumTileCount = 0;
+ }, this), idleTime);
+ },
+
+ clearPreFetch: function () {
+ this.clearTilesPreFetcher();
+ if (this._preFetchIdle !== undefined) {
+ clearTimeout(this._preFetchIdle);
+ this._preFetchIdle = undefined;
+ }
+ },
+
+ clearTilesPreFetcher: function () {
+ if (this._tilesPreFetcher !== undefined) {
+ clearInterval(this._tilesPreFetcher);
+ this._tilesPreFetcher = undefined;
+ }
+ },
+
+});
+
+L.TilesPreFetcher.PaneBorder = L.Class.extend({
+
+ initialize: function(paneBorder, paneXFixed, paneYFixed) {
+ this._border = paneBorder;
+ this._xFixed = paneXFixed;
+ this._yFixed = paneYFixed;
+ this._index = 0;
+ },
+
+ getBorderIndex: function () {
+ return this._index;
+ },
+
+ incBorderIndex: function () {
+ this._index += 1;
+ },
+
+ getBorderBounds: function () {
+ return this._border;
+ },
+
+ isXFixed: function () {
+ return this._xFixed;
+ },
+
+ isYFixed: function () {
+ return this._yFixed;
+ },
+
+});
diff --git a/loleaflet/src/layer/tile/GridLayer.js b/loleaflet/src/layer/tile/GridLayer.js
index cd4f5eda2..5d3ce69db 100644
--- a/loleaflet/src/layer/tile/GridLayer.js
+++ b/loleaflet/src/layer/tile/GridLayer.js
@@ -44,9 +44,8 @@ L.GridLayer = L.Layer.extend({
map._removeZoomLimit(this);
this._container = null;
this._tileZoom = null;
- clearTimeout(this._preFetchIdle);
+ this._clearPreFetch();
clearTimeout(this._previewInvalidator);
- clearInterval(this._tilesPreFetcher);
if (this._selections) {
this._map.removeLayer(this._selections);
@@ -1178,7 +1177,7 @@ L.GridLayer = L.Layer.extend({
return true;
},
- _preFetchTiles: function () {
+ _preFetchTiles: function (forceBorderCalc) {
if (this._emptyTilesCount > 0 || !this._map) {
return;
}
@@ -1193,7 +1192,7 @@ L.GridLayer = L.Layer.extend({
maxBorderWidth = 3;
}
- if (!this._preFetchBorder) {
+ if (!this._preFetchBorder || forceBorderCalc) {
var pixelBounds = this._map.getPixelBounds(center, zoom);
tileBorderSrc = this._pxBoundsToTileRange(pixelBounds);
this._preFetchBorder = tileBorderSrc;
@@ -1301,10 +1300,24 @@ L.GridLayer = L.Layer.extend({
this._preFetchPart = this._selectedPart;
this._preFetchIdle = setTimeout(L.bind(function () {
this._tilesPreFetcher = setInterval(L.bind(this._preFetchTiles, this), interval);
- this._prefetchIdle = undefined;
+ this._preFetchIdle = undefined;
}, this), idleTime);
},
+ _clearPreFetch: function () {
+ if (this._preFetchIdle !== undefined) {
+ clearTimeout(this._preFetchIdle);
+ }
+
+ this._clearTilesPreFetcher();
+ },
+
+ _clearTilesPreFetcher: function () {
+ if (this._tilesPreFetcher !== undefined) {
+ clearInterval(this._tilesPreFetcher);
+ }
+ },
+
_coordsToPixBounds: function (coords) {
// coords.x and coords.y are the grid indices of the tile.
var topLeft = new L.Point(coords.x, coords.y)._multiplyBy(this._tileSize);
diff --git a/loleaflet/src/map/handler/Map.TouchGesture.js b/loleaflet/src/map/handler/Map.TouchGesture.js
index 15bb932d4..dc37c9c75 100644
--- a/loleaflet/src/map/handler/Map.TouchGesture.js
+++ b/loleaflet/src/map/handler/Map.TouchGesture.js
@@ -661,10 +661,9 @@ L.Map.TouchGesture = L.Handler.extend({
this._map.dragging._draggable._onMove(e);
- // Updates the tiles
- clearInterval(this._map._docLayer._tilesPreFetcher);
- this._map._docLayer._preFetchBorder = null;
- this._map._docLayer._preFetchTiles();
+ // Prefetch border tiles for the current visible area after cancelling any scheduled calls to the prefetcher.
+ this._map._docLayer._clearPreFetch();
+ this._map._docLayer._preFetchTiles(true /* forceBorderCalc */);
if (!horizontalEnd || !verticalEnd) {
this.autoscrollAnimReq = L.Util.requestAnimFrame(this._autoscroll, this, true);
More information about the Libreoffice-commits
mailing list