Files
openlayers/src/ol/renderer/canvas/ImageLayer.js
mike-000 9a8b9d8ade add Image source imageSmoothing option
Add Image smoothing checkbox to example

Test imageSmoothing option
2020-05-09 10:41:20 +01:00

223 lines
6.2 KiB
JavaScript

/**
* @module ol/renderer/canvas/ImageLayer
*/
import CanvasLayerRenderer from './Layer.js';
import ViewHint from '../../ViewHint.js';
import {ENABLE_RASTER_REPROJECTION} from '../../reproj/common.js';
import {assign} from '../../obj.js';
import {compose as composeTransform, makeInverse} from '../../transform.js';
import {containsExtent, intersects} from '../../extent.js';
import {createTransformString} from '../../render/canvas.js';
import {fromUserExtent} from '../../proj.js';
import {getIntersection, isEmpty} from '../../extent.js';
/**
* @classdesc
* Canvas renderer for image layers.
* @api
*/
class CanvasImageLayerRenderer extends CanvasLayerRenderer {
/**
* @param {import("../../layer/Image.js").default} imageLayer Image layer.
*/
constructor(imageLayer) {
super(imageLayer);
/**
* @protected
* @type {?import("../../ImageBase.js").default}
*/
this.image_ = null;
}
/**
* @return {HTMLCanvasElement|HTMLImageElement|HTMLVideoElement} Image.
*/
getImage() {
return !this.image_ ? null : this.image_.getImage();
}
/**
* Determine whether render should be called.
* @param {import("../../PluggableMap.js").FrameState} frameState Frame state.
* @return {boolean} Layer is ready to be rendered.
*/
prepareFrame(frameState) {
const layerState = frameState.layerStatesArray[frameState.layerIndex];
const pixelRatio = frameState.pixelRatio;
const viewState = frameState.viewState;
const viewResolution = viewState.resolution;
const imageSource = this.getLayer().getSource();
const hints = frameState.viewHints;
let renderedExtent = frameState.extent;
if (layerState.extent !== undefined) {
renderedExtent = getIntersection(
renderedExtent,
fromUserExtent(layerState.extent, viewState.projection)
);
}
if (
!hints[ViewHint.ANIMATING] &&
!hints[ViewHint.INTERACTING] &&
!isEmpty(renderedExtent)
) {
if (imageSource) {
let projection = viewState.projection;
if (!ENABLE_RASTER_REPROJECTION) {
const sourceProjection = imageSource.getProjection();
if (sourceProjection) {
projection = sourceProjection;
}
}
const image = imageSource.getImage(
renderedExtent,
viewResolution,
pixelRatio,
projection
);
if (image && this.loadImage(image)) {
this.image_ = image;
}
} else {
this.image_ = null;
}
}
return !!this.image_;
}
/**
* Render the layer.
* @param {import("../../PluggableMap.js").FrameState} frameState Frame state.
* @param {HTMLElement} target Target that may be used to render content to.
* @return {HTMLElement} The rendered element.
*/
renderFrame(frameState, target) {
const image = this.image_;
const imageExtent = image.getExtent();
const imageResolution = image.getResolution();
const imagePixelRatio = image.getPixelRatio();
const layerState = frameState.layerStatesArray[frameState.layerIndex];
const pixelRatio = frameState.pixelRatio;
const viewState = frameState.viewState;
const viewCenter = viewState.center;
const viewResolution = viewState.resolution;
const size = frameState.size;
const scale =
(pixelRatio * imageResolution) / (viewResolution * imagePixelRatio);
let width = Math.round(size[0] * pixelRatio);
let height = Math.round(size[1] * pixelRatio);
const rotation = viewState.rotation;
if (rotation) {
const size = Math.round(Math.sqrt(width * width + height * height));
width = size;
height = size;
}
// set forward and inverse pixel transforms
composeTransform(
this.pixelTransform,
frameState.size[0] / 2,
frameState.size[1] / 2,
1 / pixelRatio,
1 / pixelRatio,
rotation,
-width / 2,
-height / 2
);
makeInverse(this.inversePixelTransform, this.pixelTransform);
const canvasTransform = createTransformString(this.pixelTransform);
this.useContainer(target, canvasTransform, layerState.opacity);
const context = this.context;
const canvas = context.canvas;
if (canvas.width != width || canvas.height != height) {
canvas.width = width;
canvas.height = height;
} else if (!this.containerReused) {
context.clearRect(0, 0, width, height);
}
// clipped rendering if layer extent is set
let clipped = false;
if (layerState.extent) {
const layerExtent = fromUserExtent(
layerState.extent,
viewState.projection
);
clipped =
!containsExtent(layerExtent, frameState.extent) &&
intersects(layerExtent, frameState.extent);
if (clipped) {
this.clipUnrotated(context, frameState, layerExtent);
}
}
const img = image.getImage();
const transform = composeTransform(
this.tempTransform,
width / 2,
height / 2,
scale,
scale,
0,
(imagePixelRatio * (imageExtent[0] - viewCenter[0])) / imageResolution,
(imagePixelRatio * (viewCenter[1] - imageExtent[3])) / imageResolution
);
this.renderedResolution = (imageResolution * pixelRatio) / imagePixelRatio;
const dx = transform[4];
const dy = transform[5];
const dw = img.width * transform[0];
const dh = img.height * transform[3];
assign(context, this.getLayer().getSource().getContextOptions());
this.preRender(context, frameState);
if (dw >= 0.5 && dh >= 0.5) {
const opacity = layerState.opacity;
let previousAlpha;
if (opacity !== 1) {
previousAlpha = this.context.globalAlpha;
this.context.globalAlpha = opacity;
}
this.context.drawImage(
img,
0,
0,
+img.width,
+img.height,
Math.round(dx),
Math.round(dy),
Math.round(dw),
Math.round(dh)
);
if (opacity !== 1) {
this.context.globalAlpha = previousAlpha;
}
}
this.postRender(context, frameState);
if (clipped) {
context.restore();
}
if (canvasTransform !== canvas.style.transform) {
canvas.style.transform = canvasTransform;
}
return this.container;
}
}
export default CanvasImageLayerRenderer;