diff --git a/src/ol/render/canvas/replaygroup.js b/src/ol/render/canvas/replaygroup.js
index 9250a55887..3614a54244 100644
--- a/src/ol/render/canvas/replaygroup.js
+++ b/src/ol/render/canvas/replaygroup.js
@@ -65,38 +65,65 @@ ol.render.canvas.ReplayGroup = function(
* Object.
>}
*/
this.replaysByZIndex_ = {};
+
+ /**
+ * @private
+ * @type {CanvasRenderingContext2D}
+ */
+ this.hitDetectionContext_ = ol.dom.createCanvasContext2D(1, 1);
};
ol.inherits(ol.render.canvas.ReplayGroup, ol.render.ReplayGroup);
+
/**
- * This methods creates a circle inside a fitting array. Points inside the
- * circle are marked by true, points on the outside are marked with false.
- * It uses the midpoint circle algorithm.
- * @param {number} radius Radius.
- * @returns {Array.>} an array with marked circel points.
+ * @type {Object.>>}
* @private
*/
-ol.render.canvas.ReplayGroup.createPixelCircle_ = function(radius) {
+ol.render.canvas.ReplayGroup.circleArrayCache_ = {
+ 0: [[true]]
+};
+
+
+/**
+ * This method fills a row in the array from the given coordinate to the
+ * middle with `true`.
+ * @param {Array.>} array The array that will be altered.
+ * @param {number} x X coordinate.
+ * @param {number} y Y coordinate.
+ * @private
+ */
+ol.render.canvas.ReplayGroup.fillCircleArrayRowToMiddle_ = function(array, x, y) {
+ var i;
+ var radius = Math.floor(array.length / 2);
+ if (x >= radius) {
+ for (i = radius; i < x; i++) {
+ array[i][y] = true;
+ }
+ } else if (x < radius) {
+ for (i = x + 1; i < radius; i++) {
+ array[i][y] = true;
+ }
+ }
+};
+
+
+/**
+ * This methods creates a circle inside a fitting array. Points inside the
+ * circle are marked by true, points on the outside are undefined.
+ * It uses the midpoint circle algorithm.
+ * @param {number} radius Radius.
+ * @returns {Array.>} An array with marked circle points.
+ * @private
+ */
+ol.render.canvas.ReplayGroup.getCircleArray_ = function(radius) {
+ if (ol.render.canvas.ReplayGroup.circleArrayCache_[radius] !== undefined) {
+ return ol.render.canvas.ReplayGroup.circleArrayCache_[radius];
+ }
+
var arraySize = radius * 2 + 1;
var arr = new Array(arraySize);
for (var i = 0; i < arraySize; i++) {
arr[i] = new Array(arraySize);
- for (var j = 0; j < arraySize; j++) {
- arr[i][j] = false;
- }
- }
-
- function fillRowToMiddle(x, y) {
- var i;
- if (x >= radius) {
- for (i = radius; i < x; i++) {
- arr[i][y] = true;
- }
- } else if (x < radius) {
- for (i = x + 1; i < radius; i++) {
- arr[i][y] = true;
- }
- }
}
var x = radius;
@@ -104,14 +131,14 @@ ol.render.canvas.ReplayGroup.createPixelCircle_ = function(radius) {
var error = 0;
while (x >= y) {
- fillRowToMiddle(radius + x, radius + y);
- fillRowToMiddle(radius + y, radius + x);
- fillRowToMiddle(radius - y, radius + x);
- fillRowToMiddle(radius - x, radius + y);
- fillRowToMiddle(radius - x, radius - y);
- fillRowToMiddle(radius - y, radius - x);
- fillRowToMiddle(radius + y, radius - x);
- fillRowToMiddle(radius + x, radius - y);
+ ol.render.canvas.ReplayGroup.fillCircleArrayRowToMiddle_(arr, radius + x, radius + y);
+ ol.render.canvas.ReplayGroup.fillCircleArrayRowToMiddle_(arr, radius + y, radius + x);
+ ol.render.canvas.ReplayGroup.fillCircleArrayRowToMiddle_(arr, radius - y, radius + x);
+ ol.render.canvas.ReplayGroup.fillCircleArrayRowToMiddle_(arr, radius - x, radius + y);
+ ol.render.canvas.ReplayGroup.fillCircleArrayRowToMiddle_(arr, radius - x, radius - y);
+ ol.render.canvas.ReplayGroup.fillCircleArrayRowToMiddle_(arr, radius - y, radius - x);
+ ol.render.canvas.ReplayGroup.fillCircleArrayRowToMiddle_(arr, radius + y, radius - x);
+ ol.render.canvas.ReplayGroup.fillCircleArrayRowToMiddle_(arr, radius + x, radius - y);
y++;
error += 1 + 2 * y;
@@ -121,6 +148,7 @@ ol.render.canvas.ReplayGroup.createPixelCircle_ = function(radius) {
}
}
+ ol.render.canvas.ReplayGroup.circleArrayCache_[radius] = arr;
return arr;
};
@@ -143,7 +171,7 @@ ol.render.canvas.ReplayGroup.prototype.finish = function() {
* @param {ol.Coordinate} coordinate Coordinate.
* @param {number} resolution Resolution.
* @param {number} rotation Rotation.
- * @param {number} hitTolerance hit tolerance.
+ * @param {number} hitTolerance Hit tolerance.
* @param {Object.} skippedFeaturesHash Ids of features
* to skip.
* @param {function((ol.Feature|ol.render.Feature)): T} callback Feature
@@ -155,16 +183,15 @@ ol.render.canvas.ReplayGroup.prototype.forEachFeatureAtCoordinate = function(
coordinate, resolution, rotation, hitTolerance, skippedFeaturesHash, callback) {
hitTolerance = Math.round(hitTolerance);
-
var contextSize = hitTolerance * 2 + 1;
-
var transform = ol.transform.compose(ol.transform.create(),
hitTolerance + 0.5, hitTolerance + 0.5,
1 / resolution, -1 / resolution,
-rotation,
-coordinate[0], -coordinate[1]);
-
- var context = ol.dom.createCanvasContext2D(contextSize, contextSize);
+ var context = this.hitDetectionContext_;
+ context.canvas.width = contextSize;
+ context.canvas.height = contextSize;
context.clearRect(0, 0, contextSize, contextSize);
/**
@@ -177,12 +204,7 @@ ol.render.canvas.ReplayGroup.prototype.forEachFeatureAtCoordinate = function(
ol.extent.buffer(hitExtent, resolution * this.renderBuffer_, hitExtent);
}
- var mask;
- if (hitTolerance === 0) {
- mask = [[true]];
- } else {
- mask = ol.render.canvas.ReplayGroup.createPixelCircle_(hitTolerance);
- }
+ var mask = ol.render.canvas.ReplayGroup.getCircleArray_(hitTolerance);
return this.replayHitDetection_(context, transform, rotation,
skippedFeaturesHash,
@@ -191,23 +213,22 @@ ol.render.canvas.ReplayGroup.prototype.forEachFeatureAtCoordinate = function(
* @return {?} Callback result.
*/
function(feature) {
- var imageData;
+ var imageData = context.getImageData(0, 0, contextSize, contextSize).data;
for (var i = 0; i < contextSize; i++) {
for (var j = 0; j < contextSize; j++) {
if (mask[i][j]) {
- imageData = context.getImageData(i, j, i + 1, j + 1).data;
- if (imageData[3] > 0) {
+ if (imageData[(j * contextSize + i) * 4 + 3] > 0) {
var result = callback(feature);
if (result) {
return result;
+ } else {
+ context.clearRect(0, 0, contextSize, contextSize);
+ return undefined;
}
- i = contextSize;
- j = contextSize;
}
}
}
}
- context.clearRect(0, 0, contextSize, contextSize);
}, hitExtent);
};