Files
openlayers/src/ol/renderer/canvas/IntermediateCanvas.js
2018-10-11 15:22:48 +02:00

147 lines
4.5 KiB
JavaScript

/**
* @module ol/renderer/canvas/IntermediateCanvas
*/
import {scale as scaleCoordinate} from '../../coordinate.js';
import {createCanvasContext2D} from '../../dom.js';
import {containsExtent, intersects} from '../../extent.js';
import {VOID} from '../../functions.js';
import CanvasLayerRenderer from '../canvas/Layer.js';
import {create as createTransform, apply as applyTransform} from '../../transform.js';
class IntermediateCanvasRenderer extends CanvasLayerRenderer {
/**
* @param {import("../../layer/Layer.js").default} layer Layer.
*/
constructor(layer) {
super(layer);
/**
* @protected
* @type {import("../../transform.js").Transform}
*/
this.coordinateToCanvasPixelTransform = createTransform();
/**
* @private
* @type {CanvasRenderingContext2D}
*/
this.hitCanvasContext_ = null;
}
/**
* @inheritDoc
*/
composeFrame(frameState, layerState, context) {
this.preCompose(context, frameState);
const image = this.getImage();
if (image) {
// clipped rendering if layer extent is set
const extent = layerState.extent;
const clipped = extent !== undefined &&
!containsExtent(extent, frameState.extent) &&
intersects(extent, frameState.extent);
if (clipped) {
this.clip(context, frameState, /** @type {import("../../extent.js").Extent} */ (extent));
}
const imageTransform = this.getImageTransform();
// 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
const alpha = context.globalAlpha;
context.globalAlpha = layerState.opacity;
// for performance reasons, context.setTransform is only used
// when the view is rotated. see http://jsperf.com/canvas-transform
const dx = imageTransform[4];
const dy = imageTransform[5];
const dw = image.width * imageTransform[0];
const dh = image.height * imageTransform[3];
if (dw >= 0.5 && dh >= 0.5) {
context.drawImage(image, 0, 0, +image.width, +image.height,
Math.round(dx), Math.round(dy), Math.round(dw), Math.round(dh));
}
context.globalAlpha = alpha;
if (clipped) {
context.restore();
}
}
this.postCompose(context, frameState, layerState);
}
/**
* @abstract
* @return {HTMLCanvasElement|HTMLVideoElement|HTMLImageElement} Canvas.
*/
getImage() {}
/**
* @abstract
* @return {!import("../../transform.js").Transform} Image transform.
*/
getImageTransform() {}
/**
* @inheritDoc
*/
forEachFeatureAtCoordinate(coordinate, frameState, hitTolerance, callback, thisArg) {
const layer = this.getLayer();
const source = layer.getSource();
const resolution = frameState.viewState.resolution;
const rotation = frameState.viewState.rotation;
const skippedFeatureUids = frameState.skippedFeatureUids;
return source.forEachFeatureAtCoordinate(
coordinate, resolution, rotation, hitTolerance, skippedFeatureUids,
/**
* @param {import("../../Feature.js").FeatureLike} feature Feature.
* @return {?} Callback result.
*/
function(feature) {
return callback.call(thisArg, feature, layer);
});
}
/**
* @inheritDoc
*/
forEachLayerAtCoordinate(coordinate, frameState, hitTolerance, callback, thisArg) {
if (!this.getImage()) {
return undefined;
}
if (this.getLayer().getSource().forEachFeatureAtCoordinate !== VOID) {
// for ImageCanvas sources use the original hit-detection logic,
// so that for example also transparent polygons are detected
return super.forEachLayerAtCoordinate(arguments);
} else {
const pixel = applyTransform(this.coordinateToCanvasPixelTransform, coordinate.slice());
scaleCoordinate(pixel, frameState.viewState.resolution / this.renderedResolution);
if (!this.hitCanvasContext_) {
this.hitCanvasContext_ = createCanvasContext2D(1, 1);
}
this.hitCanvasContext_.clearRect(0, 0, 1, 1);
this.hitCanvasContext_.drawImage(this.getImage(), pixel[0], pixel[1], 1, 1, 0, 0, 1, 1);
const imageData = this.hitCanvasContext_.getImageData(0, 0, 1, 1).data;
if (imageData[3] > 0) {
return callback.call(thisArg, this.getLayer(), imageData);
} else {
return undefined;
}
}
}
}
export default IntermediateCanvasRenderer;