Render tiles directly to the map canvas
This change removes the overhead of rendering tiles to an intermediate canvas.
This commit is contained in:
@@ -1,20 +1,17 @@
|
||||
// FIXME find correct globalCompositeOperation
|
||||
// FIXME optimize :-)
|
||||
|
||||
goog.provide('ol.renderer.canvas.TileLayer');
|
||||
|
||||
goog.require('goog.asserts');
|
||||
goog.require('goog.vec.Mat4');
|
||||
goog.require('ol.Size');
|
||||
goog.require('ol.TileRange');
|
||||
goog.require('ol.TileState');
|
||||
goog.require('ol.array');
|
||||
goog.require('ol.dom');
|
||||
goog.require('ol.extent');
|
||||
goog.require('ol.layer.Tile');
|
||||
goog.require('ol.render.EventType');
|
||||
goog.require('ol.renderer.canvas.Layer');
|
||||
goog.require('ol.size');
|
||||
goog.require('ol.vec.Mat4');
|
||||
goog.require('ol.source.Tile');
|
||||
|
||||
|
||||
/**
|
||||
@@ -28,63 +25,15 @@ ol.renderer.canvas.TileLayer = function(tileLayer) {
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {HTMLCanvasElement}
|
||||
* @type {Object.<string, Array.<ol.Extent>>}
|
||||
*/
|
||||
this.canvas_ = null;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {ol.Size}
|
||||
*/
|
||||
this.canvasSize_ = null;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.canvasTooBig_ = false;
|
||||
this.clipExtents_ = null;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {CanvasRenderingContext2D}
|
||||
*/
|
||||
this.context_ = null;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {!goog.vec.Mat4.Number}
|
||||
*/
|
||||
this.imageTransform_ = goog.vec.Mat4.createNumber();
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {?goog.vec.Mat4.Number}
|
||||
*/
|
||||
this.imageTransformInv_ = null;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {number}
|
||||
*/
|
||||
this.renderedCanvasZ_ = NaN;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {number}
|
||||
*/
|
||||
this.renderedTileWidth_ = NaN;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {number}
|
||||
*/
|
||||
this.renderedTileHeight_ = NaN;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {ol.TileRange}
|
||||
*/
|
||||
this.renderedCanvasTileRange_ = null;
|
||||
this.context_ = ol.dom.createCanvasContext2D();
|
||||
|
||||
/**
|
||||
* @private
|
||||
@@ -94,9 +43,9 @@ ol.renderer.canvas.TileLayer = function(tileLayer) {
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {ol.Size}
|
||||
* @type {ol.Extent}
|
||||
*/
|
||||
this.tmpSize_ = [0, 0];
|
||||
this.tmpExtent_ = ol.extent.createEmpty();
|
||||
|
||||
};
|
||||
goog.inherits(ol.renderer.canvas.TileLayer, ol.renderer.canvas.Layer);
|
||||
@@ -105,86 +54,131 @@ goog.inherits(ol.renderer.canvas.TileLayer, ol.renderer.canvas.Layer);
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
ol.renderer.canvas.TileLayer.prototype.getImage = function() {
|
||||
return this.canvas_;
|
||||
ol.renderer.canvas.TileLayer.prototype.composeFrame = function(
|
||||
frameState, layerState, context) {
|
||||
var pixelRatio = frameState.pixelRatio;
|
||||
var viewState = frameState.viewState;
|
||||
var center = viewState.center;
|
||||
var projection = viewState.projection;
|
||||
var resolution = viewState.resolution;
|
||||
var rotation = viewState.rotation;
|
||||
var size = frameState.size;
|
||||
var pixelScale = pixelRatio / resolution;
|
||||
var layer = this.getLayer();
|
||||
var source = layer.getSource();
|
||||
goog.asserts.assertInstanceof(source, ol.source.Tile,
|
||||
'source is an ol.source.Tile');
|
||||
var tileGutter = source.getGutter();
|
||||
var opaque = source.getOpaque(projection);
|
||||
|
||||
var transform = this.getTransform(frameState, 0);
|
||||
|
||||
this.dispatchPreComposeEvent(context, frameState, transform);
|
||||
|
||||
var renderContext;
|
||||
if (layer.hasListener(ol.render.EventType.RENDER)) {
|
||||
// resize and clear
|
||||
this.context_.canvas.width = context.canvas.width;
|
||||
this.context_.canvas.height = context.canvas.height;
|
||||
renderContext = this.context_;
|
||||
} else {
|
||||
renderContext = context;
|
||||
}
|
||||
var offsetX = Math.round(pixelRatio * size[0] / 2);
|
||||
var offsetY = Math.round(pixelRatio * size[1] / 2);
|
||||
// Sub-pixel overlap between tiles to avoid gaps
|
||||
var overlap = (rotation * 180 / Math.PI) % 90 === 0 ? 0 :
|
||||
opaque ? 0.25 : 0.125;
|
||||
|
||||
renderContext.translate(offsetX, offsetY);
|
||||
renderContext.rotate(rotation);
|
||||
|
||||
// for performance reasons, context.save / context.restore is not used
|
||||
// to save and restore the transformation matrix and the opacity.
|
||||
// see http://jsperf.com/context-save-restore-versus-variable
|
||||
var alpha = renderContext.globalAlpha;
|
||||
renderContext.globalAlpha = layerState.opacity;
|
||||
|
||||
var tileGrid = source.getTileGridForProjection(projection);
|
||||
var tilesToDraw = this.renderedTiles_;
|
||||
|
||||
var clipExtents, clipExtent, currentZ, i, ii, j, jj, insertPoint;
|
||||
var origin, tile, tileExtent, tileHeight, tileOffsetX, tileOffsetY;
|
||||
var tilePixelSize, tileWidth;
|
||||
for (i = 0, ii = tilesToDraw.length; i < ii; ++i) {
|
||||
tile = tilesToDraw[i];
|
||||
tileExtent = tileGrid.getTileCoordExtent(
|
||||
tile.getTileCoord(), this.tmpExtent_);
|
||||
clipExtents = !opaque && this.clipExtents_[tile.tileCoord.toString()];
|
||||
if (clipExtents) {
|
||||
// Create a clip mask for regions in this low resolution tile that will be
|
||||
// filled by a higher resolution tile
|
||||
renderContext.save();
|
||||
renderContext.beginPath();
|
||||
renderContext.moveTo((tileExtent[0] - center[0]) * pixelScale,
|
||||
(center[1] - tileExtent[1]) * pixelScale);
|
||||
renderContext.lineTo((tileExtent[2] - center[0]) * pixelScale,
|
||||
(center[1] - tileExtent[1]) * pixelScale);
|
||||
renderContext.lineTo((tileExtent[2] - center[0]) * pixelScale,
|
||||
(center[1] - tileExtent[3]) * pixelScale);
|
||||
renderContext.lineTo((tileExtent[0] - center[0]) * pixelScale,
|
||||
(center[1] - tileExtent[3]) * pixelScale);
|
||||
renderContext.closePath();
|
||||
for (j = 0, jj = clipExtents.length; j < jj; ++j) {
|
||||
clipExtent = clipExtents[j];
|
||||
renderContext.moveTo((clipExtent[0] - center[0]) * pixelScale,
|
||||
(center[1] - clipExtent[1]) * pixelScale);
|
||||
renderContext.lineTo((clipExtent[0] - center[0]) * pixelScale,
|
||||
(center[1] - clipExtent[3]) * pixelScale);
|
||||
renderContext.lineTo((clipExtent[2] - center[0]) * pixelScale,
|
||||
(center[1] - clipExtent[3]) * pixelScale);
|
||||
renderContext.lineTo((clipExtent[2] - center[0]) * pixelScale,
|
||||
(center[1] - clipExtent[1]) * pixelScale);
|
||||
renderContext.closePath();
|
||||
}
|
||||
renderContext.clip();
|
||||
}
|
||||
currentZ = tile.getTileCoord()[0];
|
||||
tilePixelSize = source.getTilePixelSize(currentZ, pixelRatio, projection);
|
||||
insertPoint = ol.extent.getTopLeft(tileExtent);
|
||||
tileWidth = Math.round(ol.extent.getWidth(tileExtent) * pixelScale);
|
||||
tileHeight = Math.round(ol.extent.getHeight(tileExtent) * pixelScale);
|
||||
// Calculate all insert points from a common origin and tile widths to avoid
|
||||
// gaps caused by rounding
|
||||
origin = ol.extent.getBottomLeft(tileGrid.getTileCoordExtent(
|
||||
tileGrid.getTileCoordForCoordAndZ(center, currentZ)));
|
||||
tileOffsetX = Math.round((origin[0] - center[0]) * pixelScale);
|
||||
tileOffsetY = Math.round((center[1] - origin[1]) * pixelScale);
|
||||
renderContext.drawImage(tile.getImage(), tileGutter, tileGutter,
|
||||
tilePixelSize[0], tilePixelSize[1],
|
||||
Math.round((insertPoint[0] - origin[0]) * pixelScale / tileWidth) *
|
||||
tileWidth + tileOffsetX - overlap,
|
||||
Math.round((origin[1] - insertPoint[1]) * pixelScale / tileHeight) *
|
||||
tileHeight + tileOffsetY - overlap,
|
||||
tileWidth + 2 * overlap, tileHeight + 2 * overlap);
|
||||
if (clipExtents) {
|
||||
renderContext.restore();
|
||||
}
|
||||
}
|
||||
|
||||
renderContext.rotate(-rotation);
|
||||
renderContext.translate(-offsetX, -offsetY);
|
||||
|
||||
if (renderContext != context) {
|
||||
this.dispatchRenderEvent(renderContext, frameState, transform);
|
||||
context.drawImage(renderContext.canvas, 0, 0);
|
||||
}
|
||||
renderContext.globalAlpha = alpha;
|
||||
|
||||
this.dispatchPostComposeEvent(context, frameState, transform);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
ol.renderer.canvas.TileLayer.prototype.getImageTransform = function() {
|
||||
return this.imageTransform_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
ol.renderer.canvas.TileLayer.prototype.prepareFrame = function(frameState, layerState) {
|
||||
|
||||
//
|
||||
// Warning! You're entering a dangerous zone!
|
||||
//
|
||||
// The canvas tile layer renderering is highly optimized, hence
|
||||
// the complexity of this function. For best performance we try
|
||||
// to minimize the number of pixels to update on the canvas. This
|
||||
// includes:
|
||||
//
|
||||
// - Only drawing pixels that will be visible.
|
||||
// - Not re-drawing pixels/tiles that are already correct.
|
||||
// - Minimizing calls to clearRect.
|
||||
// - Never shrink the canvas. Just make it bigger when necessary.
|
||||
// Re-sizing the canvas also clears it, which further means
|
||||
// re-creating it (expensive).
|
||||
//
|
||||
// The various steps performed by this functions:
|
||||
//
|
||||
// - Create a canvas element if none has been created yet.
|
||||
//
|
||||
// - Make the canvas bigger if it's too small. Note that we never shrink
|
||||
// the canvas, we just make it bigger when necessary, when rotating for
|
||||
// example. Note also that the canvas always contains a whole number
|
||||
// of tiles.
|
||||
//
|
||||
// - Invalidate the canvas tile range (renderedCanvasTileRange_ = null)
|
||||
// if (1) the canvas has been enlarged, or (2) the zoom level changes,
|
||||
// or (3) the canvas tile range doesn't contain the required tile
|
||||
// range. This canvas tile range invalidation thing is related to
|
||||
// an optimization where we attempt to redraw as few pixels as
|
||||
// possible on each prepareFrame call.
|
||||
//
|
||||
// - If the canvas tile range has been invalidated we reset
|
||||
// renderedCanvasTileRange_ and reset the renderedTiles_ array.
|
||||
// The renderedTiles_ array is the structure used to determine
|
||||
// the canvas pixels that need not be redrawn from one prepareFrame
|
||||
// call to another. It records while tile has been rendered at
|
||||
// which position in the canvas.
|
||||
//
|
||||
// - We then determine the tiles to draw on the canvas. Tiles for
|
||||
// the target resolution may not be loaded yet. In that case we
|
||||
// use low-resolution/interim tiles if loaded already. And, if
|
||||
// for a non-yet-loaded tile we haven't found a corresponding
|
||||
// low-resolution tile we indicate that the pixels for that
|
||||
// tile must be cleared on the canvas. Note: determining the
|
||||
// interim tiles is based on tile extents instead of tile
|
||||
// coords, this is to be able to handler irregular tile grids.
|
||||
//
|
||||
// - We're now ready to render. We start by calling clearRect
|
||||
// for the tiles that aren't loaded yet and are not fully covered
|
||||
// by a low-resolution tile (if they're loaded, we'll draw them;
|
||||
// if they're fully covered by a low-resolution tile then there's
|
||||
// no need to clear). We then render the tiles "back to front",
|
||||
// i.e. starting with the low-resolution tiles.
|
||||
//
|
||||
// - After rendering some bookkeeping is performed (updateUsedTiles,
|
||||
// etc.). manageTilePyramid is what enqueue tiles in the tile
|
||||
// queue for loading.
|
||||
//
|
||||
// - The last step involves updating the image transform matrix,
|
||||
// which will be used by the map renderer for the final
|
||||
// composition and positioning.
|
||||
//
|
||||
ol.renderer.canvas.TileLayer.prototype.prepareFrame = function(
|
||||
frameState, layerState) {
|
||||
|
||||
var pixelRatio = frameState.pixelRatio;
|
||||
var viewState = frameState.viewState;
|
||||
@@ -195,14 +189,8 @@ ol.renderer.canvas.TileLayer.prototype.prepareFrame = function(frameState, layer
|
||||
'layer is an instance of ol.layer.Tile');
|
||||
var tileSource = tileLayer.getSource();
|
||||
var tileGrid = tileSource.getTileGridForProjection(projection);
|
||||
var tileGutter = tileSource.getGutter();
|
||||
var z = tileGrid.getZForResolution(viewState.resolution);
|
||||
var tilePixelSize =
|
||||
tileSource.getTilePixelSize(z, frameState.pixelRatio, projection);
|
||||
var tilePixelRatio = tilePixelSize[0] /
|
||||
ol.size.toSize(tileGrid.getTileSize(z), this.tmpSize_)[0];
|
||||
var tileResolution = tileGrid.getResolution(z);
|
||||
var tilePixelResolution = tileResolution / tilePixelRatio;
|
||||
var center = viewState.center;
|
||||
var extent;
|
||||
if (tileResolution == viewState.resolution) {
|
||||
@@ -224,87 +212,11 @@ ol.renderer.canvas.TileLayer.prototype.prepareFrame = function(frameState, layer
|
||||
var tileRange = tileGrid.getTileRangeForExtentAndResolution(
|
||||
extent, tileResolution);
|
||||
|
||||
var canvasWidth = tilePixelSize[0] * tileRange.getWidth();
|
||||
var canvasHeight = tilePixelSize[1] * tileRange.getHeight();
|
||||
|
||||
var canvas, context;
|
||||
if (!this.canvas_) {
|
||||
goog.asserts.assert(!this.canvasSize_,
|
||||
'canvasSize is null (because canvas is null)');
|
||||
goog.asserts.assert(!this.context_,
|
||||
'context is null (because canvas is null)');
|
||||
goog.asserts.assert(!this.renderedCanvasTileRange_,
|
||||
'renderedCanvasTileRange is null (because canvas is null)');
|
||||
context = ol.dom.createCanvasContext2D(canvasWidth, canvasHeight);
|
||||
this.canvas_ = context.canvas;
|
||||
this.canvasSize_ = [canvasWidth, canvasHeight];
|
||||
this.context_ = context;
|
||||
this.canvasTooBig_ =
|
||||
!ol.renderer.canvas.Layer.testCanvasSize(this.canvasSize_);
|
||||
} else {
|
||||
goog.asserts.assert(this.canvasSize_,
|
||||
'non-null canvasSize (because canvas is not null)');
|
||||
goog.asserts.assert(this.context_,
|
||||
'non-null context (because canvas is not null)');
|
||||
canvas = this.canvas_;
|
||||
context = this.context_;
|
||||
if (this.canvasSize_[0] < canvasWidth ||
|
||||
this.canvasSize_[1] < canvasHeight ||
|
||||
this.renderedTileWidth_ !== tilePixelSize[0] ||
|
||||
this.renderedTileHeight_ !== tilePixelSize[1] ||
|
||||
(this.canvasTooBig_ && (this.canvasSize_[0] > canvasWidth ||
|
||||
this.canvasSize_[1] > canvasHeight))) {
|
||||
// Canvas is too small or tileSize has changed, resize it.
|
||||
// We never shrink the canvas, unless
|
||||
// we know that the current canvas size exceeds the maximum size
|
||||
canvas.width = canvasWidth;
|
||||
canvas.height = canvasHeight;
|
||||
this.canvasSize_ = [canvasWidth, canvasHeight];
|
||||
this.canvasTooBig_ =
|
||||
!ol.renderer.canvas.Layer.testCanvasSize(this.canvasSize_);
|
||||
this.renderedCanvasTileRange_ = null;
|
||||
} else {
|
||||
canvasWidth = this.canvasSize_[0];
|
||||
canvasHeight = this.canvasSize_[1];
|
||||
if (z != this.renderedCanvasZ_ ||
|
||||
!this.renderedCanvasTileRange_.containsTileRange(tileRange)) {
|
||||
this.renderedCanvasTileRange_ = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var canvasTileRange, canvasTileRangeWidth, minX, minY;
|
||||
if (!this.renderedCanvasTileRange_) {
|
||||
canvasTileRangeWidth = canvasWidth / tilePixelSize[0];
|
||||
var canvasTileRangeHeight = canvasHeight / tilePixelSize[1];
|
||||
minX = tileRange.minX -
|
||||
Math.floor((canvasTileRangeWidth - tileRange.getWidth()) / 2);
|
||||
minY = tileRange.minY -
|
||||
Math.floor((canvasTileRangeHeight - tileRange.getHeight()) / 2);
|
||||
this.renderedCanvasZ_ = z;
|
||||
this.renderedTileWidth_ = tilePixelSize[0];
|
||||
this.renderedTileHeight_ = tilePixelSize[1];
|
||||
this.renderedCanvasTileRange_ = new ol.TileRange(
|
||||
minX, minX + canvasTileRangeWidth - 1,
|
||||
minY, minY + canvasTileRangeHeight - 1);
|
||||
this.renderedTiles_ =
|
||||
new Array(canvasTileRangeWidth * canvasTileRangeHeight);
|
||||
canvasTileRange = this.renderedCanvasTileRange_;
|
||||
} else {
|
||||
canvasTileRange = this.renderedCanvasTileRange_;
|
||||
canvasTileRangeWidth = canvasTileRange.getWidth();
|
||||
}
|
||||
|
||||
goog.asserts.assert(canvasTileRange.containsTileRange(tileRange),
|
||||
'tileRange is contained in canvasTileRange');
|
||||
|
||||
/**
|
||||
* @type {Object.<number, Object.<string, ol.Tile>>}
|
||||
*/
|
||||
var tilesToDrawByZ = {};
|
||||
tilesToDrawByZ[z] = {};
|
||||
/** @type {Array.<ol.Tile>} */
|
||||
var tilesToClear = [];
|
||||
|
||||
var findLoadedTiles = this.createLoadedTileFinder(
|
||||
tileSource, projection, tilesToDrawByZ);
|
||||
@@ -339,9 +251,6 @@ ol.renderer.canvas.TileLayer.prototype.prepareFrame = function(frameState, layer
|
||||
fullyLoaded = tileGrid.forEachTileCoordParentTileRange(
|
||||
tile.tileCoord, findLoadedTiles, null, tmpTileRange, tmpExtent);
|
||||
if (!fullyLoaded) {
|
||||
// FIXME we do not need to clear the tile if it is fully covered by its
|
||||
// children
|
||||
tilesToClear.push(tile);
|
||||
childTileRange = tileGrid.getTileCoordChildTileRange(
|
||||
tile.tileCoord, tmpTileRange, tmpExtent);
|
||||
if (childTileRange) {
|
||||
@@ -352,86 +261,49 @@ ol.renderer.canvas.TileLayer.prototype.prepareFrame = function(frameState, layer
|
||||
}
|
||||
}
|
||||
|
||||
var i, ii;
|
||||
for (i = 0, ii = tilesToClear.length; i < ii; ++i) {
|
||||
tile = tilesToClear[i];
|
||||
x = tilePixelSize[0] * (tile.tileCoord[1] - canvasTileRange.minX);
|
||||
y = tilePixelSize[1] * (canvasTileRange.maxY - tile.tileCoord[2]);
|
||||
context.clearRect(x, y, tilePixelSize[0], tilePixelSize[1]);
|
||||
}
|
||||
|
||||
/** @type {Array.<number>} */
|
||||
var zs = Object.keys(tilesToDrawByZ).map(Number);
|
||||
zs.sort(ol.array.numberSafeCompareFunction);
|
||||
var opaque = tileSource.getOpaque(projection);
|
||||
var origin = ol.extent.getTopLeft(tileGrid.getTileCoordExtent(
|
||||
[z, canvasTileRange.minX, canvasTileRange.maxY],
|
||||
tmpExtent));
|
||||
var currentZ, index, scale, tileCoordKey, tileExtent, tileState, tilesToDraw;
|
||||
var ix, iy, interimTileRange, maxX, maxY;
|
||||
var height, width;
|
||||
var renderables = [];
|
||||
var i, ii, currentZ, tileCoordKey, tilesToDraw;
|
||||
for (i = 0, ii = zs.length; i < ii; ++i) {
|
||||
currentZ = zs[i];
|
||||
tilePixelSize =
|
||||
tileSource.getTilePixelSize(currentZ, pixelRatio, projection);
|
||||
tilesToDraw = tilesToDrawByZ[currentZ];
|
||||
if (currentZ == z) {
|
||||
for (tileCoordKey in tilesToDraw) {
|
||||
tile = tilesToDraw[tileCoordKey];
|
||||
index =
|
||||
(tile.tileCoord[2] - canvasTileRange.minY) * canvasTileRangeWidth +
|
||||
(tile.tileCoord[1] - canvasTileRange.minX);
|
||||
if (this.renderedTiles_[index] != tile) {
|
||||
x = tilePixelSize[0] * (tile.tileCoord[1] - canvasTileRange.minX);
|
||||
y = tilePixelSize[1] * (canvasTileRange.maxY - tile.tileCoord[2]);
|
||||
tileState = tile.getState();
|
||||
if (tileState == ol.TileState.EMPTY ||
|
||||
(tileState == ol.TileState.ERROR && !useInterimTilesOnError) ||
|
||||
!opaque) {
|
||||
context.clearRect(x, y, tilePixelSize[0], tilePixelSize[1]);
|
||||
}
|
||||
if (tileState == ol.TileState.LOADED) {
|
||||
context.drawImage(tile.getImage(),
|
||||
tileGutter, tileGutter, tilePixelSize[0], tilePixelSize[1],
|
||||
x, y, tilePixelSize[0], tilePixelSize[1]);
|
||||
}
|
||||
this.renderedTiles_[index] = tile;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
scale = tileGrid.getResolution(currentZ) / tileResolution;
|
||||
for (tileCoordKey in tilesToDraw) {
|
||||
tile = tilesToDraw[tileCoordKey];
|
||||
tileExtent = tileGrid.getTileCoordExtent(tile.tileCoord, tmpExtent);
|
||||
x = (tileExtent[0] - origin[0]) / tilePixelResolution;
|
||||
y = (origin[1] - tileExtent[3]) / tilePixelResolution;
|
||||
width = scale * tilePixelSize[0];
|
||||
height = scale * tilePixelSize[1];
|
||||
tileState = tile.getState();
|
||||
if (tileState == ol.TileState.EMPTY || !opaque) {
|
||||
context.clearRect(x, y, width, height);
|
||||
}
|
||||
if (tileState == ol.TileState.LOADED) {
|
||||
context.drawImage(tile.getImage(),
|
||||
tileGutter, tileGutter, tilePixelSize[0], tilePixelSize[1],
|
||||
x, y, width, height);
|
||||
}
|
||||
interimTileRange =
|
||||
tileGrid.getTileRangeForExtentAndZ(tileExtent, z, tmpTileRange);
|
||||
minX = Math.max(interimTileRange.minX, canvasTileRange.minX);
|
||||
maxX = Math.min(interimTileRange.maxX, canvasTileRange.maxX);
|
||||
minY = Math.max(interimTileRange.minY, canvasTileRange.minY);
|
||||
maxY = Math.min(interimTileRange.maxY, canvasTileRange.maxY);
|
||||
for (ix = minX; ix <= maxX; ++ix) {
|
||||
for (iy = minY; iy <= maxY; ++iy) {
|
||||
index = (iy - canvasTileRange.minY) * canvasTileRangeWidth +
|
||||
(ix - canvasTileRange.minX);
|
||||
this.renderedTiles_[index] = undefined;
|
||||
}
|
||||
}
|
||||
for (tileCoordKey in tilesToDraw) {
|
||||
tile = tilesToDraw[tileCoordKey];
|
||||
if (tile.getState() == ol.TileState.LOADED) {
|
||||
renderables.push(tile);
|
||||
}
|
||||
}
|
||||
}
|
||||
this.renderedTiles_ = renderables;
|
||||
if (!tileSource.getOpaque(projection)) {
|
||||
var clipExtents = {};
|
||||
var tileCoord;
|
||||
for (i = renderables.length - 1; i >= 0; --i) {
|
||||
tileCoord = renderables[i].getTileCoord();
|
||||
tileGrid.forEachTileCoordParentTileRange(tileCoord,
|
||||
function(z, tileRange) {
|
||||
var tiles = tilesToDrawByZ[z];
|
||||
if (tiles) {
|
||||
var key, tile;
|
||||
for (key in tiles) {
|
||||
tile = tiles[key];
|
||||
if (tileRange.contains(tile.getTileCoord()) &&
|
||||
tile.getState() == ol.TileState.LOADED) {
|
||||
if (!(key in clipExtents)) {
|
||||
clipExtents[key] = [];
|
||||
}
|
||||
clipExtents[key].push(tileGrid.getTileCoordExtent(tileCoord));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}, this, tmpTileRange, tmpExtent);
|
||||
}
|
||||
this.clipExtents_ = clipExtents;
|
||||
}
|
||||
|
||||
this.updateUsedTiles(frameState.usedTiles, tileSource, z, tileRange);
|
||||
this.manageTilePyramid(frameState, tileSource, tileGrid, pixelRatio,
|
||||
@@ -439,16 +311,6 @@ ol.renderer.canvas.TileLayer.prototype.prepareFrame = function(frameState, layer
|
||||
this.scheduleExpireCache(frameState, tileSource);
|
||||
this.updateLogos(frameState, tileSource);
|
||||
|
||||
ol.vec.Mat4.makeTransform2D(this.imageTransform_,
|
||||
pixelRatio * frameState.size[0] / 2,
|
||||
pixelRatio * frameState.size[1] / 2,
|
||||
pixelRatio * tilePixelResolution / viewState.resolution,
|
||||
pixelRatio * tilePixelResolution / viewState.resolution,
|
||||
viewState.rotation,
|
||||
(origin[0] - center[0]) / tilePixelResolution,
|
||||
(center[1] - origin[1]) / tilePixelResolution);
|
||||
this.imageTransformInv_ = null;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
@@ -456,21 +318,16 @@ ol.renderer.canvas.TileLayer.prototype.prepareFrame = function(frameState, layer
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
ol.renderer.canvas.TileLayer.prototype.forEachLayerAtPixel = function(pixel, frameState, callback, thisArg) {
|
||||
if (!this.context_) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (!this.imageTransformInv_) {
|
||||
this.imageTransformInv_ = goog.vec.Mat4.createNumber();
|
||||
goog.vec.Mat4.invert(this.imageTransform_, this.imageTransformInv_);
|
||||
}
|
||||
|
||||
var pixelOnCanvas =
|
||||
this.getPixelOnCanvas(pixel, this.imageTransformInv_);
|
||||
ol.renderer.canvas.TileLayer.prototype.forEachLayerAtPixel = function(
|
||||
pixel, frameState, callback, thisArg) {
|
||||
var canvas = this.context_.canvas;
|
||||
var size = frameState.size;
|
||||
canvas.width = size[0];
|
||||
canvas.height = size[1];
|
||||
this.composeFrame(frameState, this.getLayer().getLayerState(), this.context_);
|
||||
|
||||
var imageData = this.context_.getImageData(
|
||||
pixelOnCanvas[0], pixelOnCanvas[1], 1, 1).data;
|
||||
pixel[0], pixel[1], 1, 1).data;
|
||||
|
||||
if (imageData[3] > 0) {
|
||||
return callback.call(thisArg, this.getLayer());
|
||||
|
||||
Reference in New Issue
Block a user