diff --git a/examples/vector-layer.js b/examples/vector-layer.js index 35b95c69b2..adda211076 100644 --- a/examples/vector-layer.js +++ b/examples/vector-layer.js @@ -40,7 +40,7 @@ var map = new ol.Map({ target: 'map', view: new ol.View2D({ center: [0, 0], - zoom: 2 + zoom: 1 }) }); diff --git a/src/objectliterals.jsdoc b/src/objectliterals.jsdoc index 622a98a221..71636f4f7c 100644 --- a/src/objectliterals.jsdoc +++ b/src/objectliterals.jsdoc @@ -25,6 +25,9 @@ * @typedef {Object} olx.MapOptions * @property {ol.Collection|Array.|undefined} controls * Controls initially added to the map. + * @property {number|undefined} devicePixelRatio The ratio between physical + * pixels and device-independent pixels (dips) on the device. If `undefined` + * then it gets set by using `window.devicePixelRatio`. * @property {ol.Collection|Array.|undefined} interactions * Interactions that are initially added to the map. * @property {Array.|ol.Collection|undefined} layers Layers. diff --git a/src/ol/framestate.js b/src/ol/framestate.js index b86c897ef9..84f3da2c46 100644 --- a/src/ol/framestate.js +++ b/src/ol/framestate.js @@ -20,6 +20,7 @@ goog.require('ol.layer.LayerState'); * @typedef {{animate: boolean, * attributions: Object., * coordinateToPixelMatrix: goog.vec.Mat4.Number, + * devicePixelRatio: number, * extent: (null|ol.Extent), * focus: ol.Coordinate, * index: number, diff --git a/src/ol/map.js b/src/ol/map.js index 1019341a02..98ea98d24e 100644 --- a/src/ol/map.js +++ b/src/ol/map.js @@ -157,6 +157,13 @@ ol.Map = function(options) { var optionsInternal = ol.Map.createOptionsInternal(options); + /** + * @private + * @type {number} + */ + this.devicePixelRatio_ = goog.isDef(options.devicePixelRatio) ? + options.devicePixelRatio : ol.BrowserFeature.DEVICE_PIXEL_RATIO; + /** * @private * @type {goog.async.AnimationDelay} @@ -1069,6 +1076,7 @@ ol.Map.prototype.renderFrame_ = function(time) { animate: false, attributions: {}, coordinateToPixelMatrix: this.coordinateToPixelMatrix_, + devicePixelRatio: this.devicePixelRatio_, extent: null, focus: goog.isNull(this.focus_) ? view2DState.center : this.focus_, index: this.frameIndex_++, diff --git a/src/ol/render/canvas/canvasimmediate.js b/src/ol/render/canvas/canvasimmediate.js index f3aa2228b5..2e1729bc29 100644 --- a/src/ol/render/canvas/canvasimmediate.js +++ b/src/ol/render/canvas/canvasimmediate.js @@ -17,11 +17,12 @@ goog.require('ol.style.Text'); * @constructor * @implements {ol.render.IRender} * @param {CanvasRenderingContext2D} context Context. + * @param {number} pixelRatio Pixel ratio. * @param {ol.Extent} extent Extent. * @param {goog.vec.Mat4.AnyType} transform Transform. * @struct */ -ol.render.canvas.Immediate = function(context, extent, transform) { +ol.render.canvas.Immediate = function(context, pixelRatio, extent, transform) { /** * @private @@ -29,6 +30,12 @@ ol.render.canvas.Immediate = function(context, extent, transform) { */ this.context_ = context; + /** + * @private + * @type {number} + */ + this.pixelRatio_ = pixelRatio; + /** * @private * @type {ol.Extent} @@ -385,8 +392,8 @@ ol.render.canvas.Immediate.prototype.setFillStrokeStyle = strokeStyle.lineDash : ol.render.canvas.defaultLineDash; state.lineJoin = goog.isDef(strokeStyle.lineJoin) ? strokeStyle.lineJoin : ol.render.canvas.defaultLineJoin; - state.lineWidth = goog.isDef(strokeStyle.width) ? - strokeStyle.width : ol.render.canvas.defaultLineWidth; + state.lineWidth = this.pixelRatio_ * (goog.isDef(strokeStyle.width) ? + strokeStyle.width : ol.render.canvas.defaultLineWidth); state.miterLimit = goog.isDef(strokeStyle.miterLimit) ? strokeStyle.miterLimit : ol.render.canvas.defaultMiterLimit; } else { diff --git a/src/ol/render/canvas/canvasreplay.js b/src/ol/render/canvas/canvasreplay.js index 8a7f7a7a12..14763abc57 100644 --- a/src/ol/render/canvas/canvasreplay.js +++ b/src/ol/render/canvas/canvasreplay.js @@ -41,10 +41,17 @@ ol.render.canvas.Instruction = { /** * @constructor * @implements {ol.render.IRender} + * @param {number} pixelRatio Pixel ratio. * @protected * @struct */ -ol.render.canvas.Replay = function() { +ol.render.canvas.Replay = function(pixelRatio) { + + /** + * @protected + * @type {number} + */ + this.pixelRatio = pixelRatio; /** * @private @@ -430,12 +437,13 @@ ol.render.canvas.Replay.prototype.setTextStyle = goog.abstractMethod; /** * @constructor * @extends {ol.render.canvas.Replay} + * @param {number} pixelRatio Pixel ratio. * @protected * @struct */ -ol.render.canvas.ImageReplay = function() { +ol.render.canvas.ImageReplay = function(pixelRatio) { - goog.base(this); + goog.base(this, pixelRatio); /** * @private @@ -588,12 +596,13 @@ ol.render.canvas.ImageReplay.prototype.setImageStyle = function(imageStyle) { /** * @constructor * @extends {ol.render.canvas.Replay} + * @param {number} pixelRatio Pixel ratio. * @protected * @struct */ -ol.render.canvas.LineStringReplay = function() { +ol.render.canvas.LineStringReplay = function(pixelRatio) { - goog.base(this); + goog.base(this, pixelRatio); /** * @private @@ -779,8 +788,8 @@ ol.render.canvas.LineStringReplay.prototype.setFillStrokeStyle = strokeStyle.lineDash : ol.render.canvas.defaultLineDash; this.state_.lineJoin = goog.isDef(strokeStyle.lineJoin) ? strokeStyle.lineJoin : ol.render.canvas.defaultLineJoin; - this.state_.lineWidth = goog.isDef(strokeStyle.width) ? - strokeStyle.width : ol.render.canvas.defaultLineWidth; + this.state_.lineWidth = this.pixelRatio * (goog.isDef(strokeStyle.width) ? + strokeStyle.width : ol.render.canvas.defaultLineWidth); this.state_.miterLimit = goog.isDef(strokeStyle.miterLimit) ? strokeStyle.miterLimit : ol.render.canvas.defaultMiterLimit; }; @@ -790,12 +799,13 @@ ol.render.canvas.LineStringReplay.prototype.setFillStrokeStyle = /** * @constructor * @extends {ol.render.canvas.Replay} + * @param {number} pixelRatio Pixel ratio. * @protected * @struct */ -ol.render.canvas.PolygonReplay = function() { +ol.render.canvas.PolygonReplay = function(pixelRatio) { - goog.base(this); + goog.base(this, pixelRatio); /** * @private @@ -990,8 +1000,8 @@ ol.render.canvas.PolygonReplay.prototype.setFillStrokeStyle = strokeStyle.lineDash : ol.render.canvas.defaultLineDash; state.lineJoin = goog.isDef(strokeStyle.lineJoin) ? strokeStyle.lineJoin : ol.render.canvas.defaultLineJoin; - state.lineWidth = goog.isDef(strokeStyle.width) ? - strokeStyle.width : ol.render.canvas.defaultLineWidth; + state.lineWidth = this.pixelRatio * (goog.isDef(strokeStyle.width) ? + strokeStyle.width : ol.render.canvas.defaultLineWidth); state.miterLimit = goog.isDef(strokeStyle.miterLimit) ? strokeStyle.miterLimit : ol.render.canvas.defaultMiterLimit; } else { @@ -1052,9 +1062,16 @@ ol.render.canvas.PolygonReplay.prototype.setFillStrokeStyles_ = function() { /** * @constructor * @implements {ol.render.IReplayGroup} + * @param {number} pixelRatio Pixel ratio. * @struct */ -ol.render.canvas.ReplayGroup = function() { +ol.render.canvas.ReplayGroup = function(pixelRatio) { + + /** + * @private + * @type {number} + */ + this.pixelRatio_ = pixelRatio; /** * @private @@ -1248,7 +1265,7 @@ ol.render.canvas.ReplayGroup.prototype.getReplay = if (!goog.isDef(replay)) { var constructor = ol.render.canvas.BATCH_CONSTRUCTORS_[replayType]; goog.asserts.assert(goog.isDef(constructor)); - replay = new constructor(); + replay = new constructor(this.pixelRatio_); replayes[replayType] = replay; } return replay; @@ -1266,7 +1283,8 @@ ol.render.canvas.ReplayGroup.prototype.isEmpty = function() { /** * @const * @private - * @type {Object.} + * @type {Object.} */ ol.render.canvas.BATCH_CONSTRUCTORS_ = { 'Image': ol.render.canvas.ImageReplay, diff --git a/src/ol/renderer/canvas/canvasimagelayerrenderer.js b/src/ol/renderer/canvas/canvasimagelayerrenderer.js index 29af98a994..dc6a94d298 100644 --- a/src/ol/renderer/canvas/canvasimagelayerrenderer.js +++ b/src/ol/renderer/canvas/canvasimagelayerrenderer.js @@ -96,9 +96,12 @@ ol.renderer.canvas.ImageLayer.prototype.prepareFrame = image = this.image_; var imageExtent = image.getExtent(); var imageResolution = image.getResolution(); + var devicePixelRatio = frameState.devicePixelRatio; ol.vec.Mat4.makeTransform2D(this.imageTransform_, - frameState.size[0] / 2, frameState.size[1] / 2, - imageResolution / viewResolution, imageResolution / viewResolution, + devicePixelRatio * frameState.size[0] / 2, + devicePixelRatio * frameState.size[1] / 2, + devicePixelRatio * imageResolution / viewResolution, + devicePixelRatio * imageResolution / viewResolution, viewRotation, (imageExtent[0] - viewCenter[0]) / imageResolution, (viewCenter[1] - imageExtent[3]) / imageResolution); diff --git a/src/ol/renderer/canvas/canvaslayerrenderer.js b/src/ol/renderer/canvas/canvaslayerrenderer.js index 7485e3f05a..f3a0366338 100644 --- a/src/ol/renderer/canvas/canvaslayerrenderer.js +++ b/src/ol/renderer/canvas/canvaslayerrenderer.js @@ -85,8 +85,8 @@ ol.renderer.canvas.Layer.prototype.dispatchComposeEvent_ = if (layer.hasListener(type)) { var transform = goog.isDef(opt_transform) ? opt_transform : this.getTransform(frameState); - var render = new ol.render.canvas.Immediate(context, frameState.extent, - transform); + var render = new ol.render.canvas.Immediate( + context, frameState.devicePixelRatio, frameState.extent, transform); var composeEvent = new ol.render.Event(type, layer, render, frameState, context, null); layer.dispatchEvent(composeEvent); @@ -139,9 +139,12 @@ ol.renderer.canvas.Layer.prototype.getImageTransform = goog.abstractMethod; */ ol.renderer.canvas.Layer.prototype.getTransform = function(frameState) { var view2DState = frameState.view2DState; + var devicePixelRatio = frameState.devicePixelRatio; return ol.vec.Mat4.makeTransform2D(this.transform_, - frameState.size[0] / 2, frameState.size[1] / 2, - 1 / view2DState.resolution, -1 / view2DState.resolution, + devicePixelRatio * frameState.size[0] / 2, + devicePixelRatio * frameState.size[1] / 2, + devicePixelRatio / view2DState.resolution, + -devicePixelRatio / view2DState.resolution, -view2DState.rotation, -view2DState.center[0], -view2DState.center[1]); }; diff --git a/src/ol/renderer/canvas/canvasmaprenderer.js b/src/ol/renderer/canvas/canvasmaprenderer.js index a78e1f7735..a842aa6482 100644 --- a/src/ol/renderer/canvas/canvasmaprenderer.js +++ b/src/ol/renderer/canvas/canvasmaprenderer.js @@ -6,6 +6,7 @@ goog.require('goog.asserts'); goog.require('goog.dom'); goog.require('goog.dom.TagName'); goog.require('goog.style'); +goog.require('goog.vec.Mat4'); goog.require('ol.css'); goog.require('ol.layer.Image'); goog.require('ol.layer.Tile'); @@ -19,6 +20,7 @@ goog.require('ol.renderer.canvas.Layer'); goog.require('ol.renderer.canvas.TileLayer'); goog.require('ol.renderer.canvas.VectorLayer'); goog.require('ol.source.State'); +goog.require('ol.vec.Mat4'); @@ -56,6 +58,12 @@ ol.renderer.canvas.Map = function(container, map) { this.context_ = /** @type {CanvasRenderingContext2D} */ (this.canvas_.getContext('2d')); + /** + * @private + * @type {!goog.vec.Mat4.Number} + */ + this.transform_ = goog.vec.Mat4.createNumber(); + }; goog.inherits(ol.renderer.canvas.Map, ol.renderer.Map); @@ -87,8 +95,17 @@ ol.renderer.canvas.Map.prototype.dispatchComposeEvent_ = var map = this.getMap(); var context = this.context_; if (map.hasListener(type)) { + var view2DState = frameState.view2DState; + var devicePixelRatio = frameState.devicePixelRatio; + ol.vec.Mat4.makeTransform2D(this.transform_, + this.canvas_.width / 2, + this.canvas_.height / 2, + devicePixelRatio / view2DState.resolution, + -devicePixelRatio / view2DState.resolution, + -view2DState.rotation, + -view2DState.center[0], -view2DState.center[1]); var render = new ol.render.canvas.Immediate( - context, frameState.extent, frameState.coordinateToPixelMatrix); + context, devicePixelRatio, frameState.extent, this.transform_); var composeEvent = new ol.render.Event(type, map, render, frameState, context, null); map.dispatchEvent(composeEvent); @@ -121,11 +138,12 @@ ol.renderer.canvas.Map.prototype.renderFrame = function(frameState) { } var context = this.context_; - - var size = frameState.size; - if (this.canvas_.width != size[0] || this.canvas_.height != size[1]) { - this.canvas_.width = size[0]; - this.canvas_.height = size[1]; + var ratio = frameState.devicePixelRatio; + var width = frameState.size[0] * ratio; + var height = frameState.size[1] * ratio; + if (this.canvas_.width != width || this.canvas_.height != height) { + this.canvas_.width = width; + this.canvas_.height = height; } else { context.clearRect(0, 0, this.canvas_.width, this.canvas_.height); } diff --git a/src/ol/renderer/canvas/canvastilelayerrenderer.js b/src/ol/renderer/canvas/canvastilelayerrenderer.js index db5919be45..c24e597615 100644 --- a/src/ol/renderer/canvas/canvastilelayerrenderer.js +++ b/src/ol/renderer/canvas/canvastilelayerrenderer.js @@ -382,10 +382,12 @@ ol.renderer.canvas.TileLayer.prototype.prepareFrame = this.scheduleExpireCache(frameState, tileSource); this.updateLogos(frameState, tileSource); + var devicePixelRatio = frameState.devicePixelRatio; ol.vec.Mat4.makeTransform2D(this.imageTransform_, - frameState.size[0] / 2, frameState.size[1] / 2, - tileResolution / view2DState.resolution, - tileResolution / view2DState.resolution, + devicePixelRatio * frameState.size[0] / 2, + devicePixelRatio * frameState.size[1] / 2, + devicePixelRatio * tileResolution / view2DState.resolution, + devicePixelRatio * tileResolution / view2DState.resolution, view2DState.rotation, (origin[0] - center[0]) / tileResolution, (center[1] - origin[1]) / tileResolution); diff --git a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js index 1061501efb..08b78a3a7f 100644 --- a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js +++ b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js @@ -172,6 +172,7 @@ ol.renderer.canvas.VectorLayer.prototype.prepareFrame = var vectorSource = vectorLayer.getVectorSource(); var frameStateExtent = frameState.extent; var frameStateResolution = frameState.view2DState.resolution; + var pixelRatio = frameState.devicePixelRatio; if (!this.dirty_ && this.renderedResolution_ == frameStateResolution && @@ -198,15 +199,15 @@ ol.renderer.canvas.VectorLayer.prototype.prepareFrame = if (!goog.isDef(styleFunction)) { styleFunction = ol.layer.Vector.defaultStyleFunction; } - var replayGroup = new ol.render.canvas.ReplayGroup(); + var replayGroup = new ol.render.canvas.ReplayGroup(pixelRatio); vectorSource.forEachFeatureInExtent(extent, /** * @param {ol.Feature} feature Feature. */ function(feature) { this.dirty_ = this.dirty_ || - this.renderFeature(feature, frameStateResolution, styleFunction, - replayGroup); + this.renderFeature(feature, frameStateResolution, pixelRatio, + styleFunction, replayGroup); }, this); replayGroup.finish(); @@ -223,20 +224,22 @@ ol.renderer.canvas.VectorLayer.prototype.prepareFrame = /** * @param {ol.Feature} feature Feature. * @param {number} resolution Resolution. + * @param {number} pixelRatio Pixel ratio. * @param {ol.style.StyleFunction} styleFunction Style function. * @param {ol.render.canvas.ReplayGroup} replayGroup Replay group. * @return {boolean} `true` if an image is loading. */ ol.renderer.canvas.VectorLayer.prototype.renderFeature = - function(feature, resolution, styleFunction, replayGroup) { + function(feature, resolution, pixelRatio, styleFunction, replayGroup) { var loading = false; var styles = styleFunction(feature, resolution); // FIXME if styles is null, should we use the default style? if (!goog.isDefAndNotNull(styles)) { return false; } - // simplify to a tolerance of half a CSS pixel - var squaredTolerance = resolution * resolution / 4; + // simplify to a tolerance of half a device pixel + var squaredTolerance = + resolution * resolution / (4 * pixelRatio * pixelRatio); var i, ii, style, imageStyle, imageState; for (i = 0, ii = styles.length; i < ii; ++i) { style = styles[i];