diff --git a/src/objectliterals.jsdoc b/src/objectliterals.jsdoc index 65de88345b..f98b9dfbe3 100644 --- a/src/objectliterals.jsdoc +++ b/src/objectliterals.jsdoc @@ -735,6 +735,7 @@ * @typedef {Object} olx.style.IconOptions * @property {ol.Pixel|undefined} anchor Anchor. * @property {null|string|undefined} crossOrigin crossOrigin setting for image. + * @property {number|undefined} scale Scale. * @property {number|undefined} rotation Rotation. * @property {ol.Size|undefined} size Icon size in pixel. * @property {string} src Image source URI. diff --git a/src/ol/render/canvas/canvasimmediate.js b/src/ol/render/canvas/canvasimmediate.js index 85b3260a82..9736439c03 100644 --- a/src/ol/render/canvas/canvasimmediate.js +++ b/src/ol/render/canvas/canvasimmediate.js @@ -5,11 +5,13 @@ goog.provide('ol.render.canvas.Immediate'); goog.require('goog.asserts'); +goog.require('goog.vec.Mat4'); goog.require('ol.color'); goog.require('ol.extent'); goog.require('ol.render.IRender'); goog.require('ol.render.canvas'); goog.require('ol.style.Text'); +goog.require('ol.vec.Mat4'); @@ -60,11 +62,13 @@ ol.render.canvas.Immediate = function(context, pixelRatio, extent, transform) { * fillStyle: (string|undefined), * strokeStyle: (string|undefined), * lineWidth: (number|undefined), + * image: (HTMLCanvasElement|HTMLVideoElement|Image), * anchorX: (number|undefined), * anchorY: (number|undefined), - * image: (HTMLCanvasElement|HTMLVideoElement|Image), * height: (number|undefined), * width: (number|undefined), + * scale: number, + * rotation: number, * lineCap: (string|undefined), * lineDash: Array., * lineJoin: (string|undefined), @@ -83,10 +87,12 @@ ol.render.canvas.Immediate = function(context, pixelRatio, extent, transform) { fillStyle: undefined, strokeStyle: undefined, lineWidth: undefined, + image: null, anchorX: undefined, anchorY: undefined, - image: null, height: undefined, + rotation: 0, + scale: 1, width: undefined, lineCap: undefined, lineDash: null, @@ -102,6 +108,12 @@ ol.render.canvas.Immediate = function(context, pixelRatio, extent, transform) { */ this.pixelCoordinates_ = []; + /** + * @private + * @type {!goog.vec.Mat4.Number} + */ + this.tmpLocalTransform_ = goog.vec.Mat4.createNumber(); + }; @@ -122,6 +134,7 @@ ol.render.canvas.Immediate.prototype.drawImages_ = function(geometry) { goog.asserts.assert(goog.isDef(state.width)); var pixelCoordinates = ol.geom.transformSimpleGeometry2D( geometry, this.transform_, this.pixelCoordinates_); + var localTransform = this.tmpLocalTransform_; var i, ii; for (i = 0, ii = pixelCoordinates.length; i < ii; i += 2) { var x = pixelCoordinates[i] - state.anchorX; @@ -130,8 +143,22 @@ ol.render.canvas.Immediate.prototype.drawImages_ = function(geometry) { x = (x + 0.5) | 0; y = (y + 0.5) | 0; } + if (state.scale != 1 || state.rotation !== 0) { + ol.vec.Mat4.makeTransform2D(localTransform, + x, y, state.scale, state.scale, state.rotation, -x, -y); + context.setTransform( + goog.vec.Mat4.getElement(localTransform, 0, 0), + goog.vec.Mat4.getElement(localTransform, 1, 0), + goog.vec.Mat4.getElement(localTransform, 0, 1), + goog.vec.Mat4.getElement(localTransform, 1, 1), + goog.vec.Mat4.getElement(localTransform, 0, 3), + goog.vec.Mat4.getElement(localTransform, 1, 3)); + } context.drawImage(state.image, x, y, state.width, state.height); } + if (state.scale != 1 || state.rotation !== 0) { + context.setTransform(1, 0, 0, 1, 0, 0); + } }; @@ -469,12 +496,14 @@ ol.render.canvas.Immediate.prototype.setImageStyle = function(imageStyle) { var image = imageStyle.getImage(1); goog.asserts.assert(!goog.isNull(image)); var state = this.state_; + state.image = image; state.anchorX = anchor[0]; state.anchorY = anchor[1]; - state.image = image; - state.width = size[0]; state.height = size[1]; + state.rotation = imageStyle.getRotation(); + state.scale = imageStyle.getScale(); state.snapToPixel = imageStyle.getSnapToPixel(); + state.width = size[0]; } }; diff --git a/src/ol/render/canvas/canvasreplay.js b/src/ol/render/canvas/canvasreplay.js index 8a72a97353..9a63c29df4 100644 --- a/src/ol/render/canvas/canvasreplay.js +++ b/src/ol/render/canvas/canvasreplay.js @@ -109,6 +109,12 @@ ol.render.canvas.Replay = function(pixelRatio, tolerance) { */ this.extent_ = ol.extent.createEmpty(); + /** + * @private + * @type {!goog.vec.Mat4.Number} + */ + this.tmpLocalTransform_ = goog.vec.Mat4.createNumber(); + }; @@ -180,6 +186,7 @@ ol.render.canvas.Replay.prototype.replay_ = var ii = instructions.length; // end of instructions var d; // data index var dd; // end of per-instruction data + var localTransform = this.tmpLocalTransform_; while (i < ii) { var instruction = instructions[i]; var type = /** @type {ol.render.canvas.Instruction} */ (instruction[0]); @@ -202,13 +209,16 @@ ol.render.canvas.Replay.prototype.replay_ = d = /** @type {number} */ (instruction[1]); goog.asserts.assert(goog.isNumber(instruction[2])); dd = /** @type {number} */ (instruction[2]); - var anchorX = /** @type {number} */ (instruction[3]); - var anchorY = /** @type {number} */ (instruction[4]); - var width = /** @type {number} */ (instruction[5]); - var height = /** @type {number} */ (instruction[6]); var image = /** @type {HTMLCanvasElement|HTMLVideoElement|Image} */ - (instruction[7]); - var snapToPixel = /** @type {boolean|undefined} */ (instruction[8]); + (instruction[3]); + // Remaining arguments in DRAW_IMAGE are in alphabetical order + var anchorX = /** @type {number} */ (instruction[4]); + var anchorY = /** @type {number} */ (instruction[5]); + var height = /** @type {number} */ (instruction[6]); + var rotation = /** @type {number} */ (instruction[7]); + var scale = /** @type {number} */ (instruction[8]); + var snapToPixel = /** @type {boolean|undefined} */ (instruction[9]); + var width = /** @type {number} */ (instruction[10]); for (; d < dd; d += 2) { var x = pixelCoordinates[d] - anchorX; var y = pixelCoordinates[d + 1] - anchorY; @@ -216,7 +226,21 @@ ol.render.canvas.Replay.prototype.replay_ = x = (x + 0.5) | 0; y = (y + 0.5) | 0; } + if (scale != 1 || rotation !== 0) { + ol.vec.Mat4.makeTransform2D( + localTransform, x, y, scale, scale, rotation, -x, -y); + context.setTransform( + goog.vec.Mat4.getElement(localTransform, 0, 0), + goog.vec.Mat4.getElement(localTransform, 1, 0), + goog.vec.Mat4.getElement(localTransform, 0, 1), + goog.vec.Mat4.getElement(localTransform, 1, 1), + goog.vec.Mat4.getElement(localTransform, 0, 3), + goog.vec.Mat4.getElement(localTransform, 1, 3)); + } context.drawImage(image, x, y, width, height); + if (scale != 1 || rotation !== 0) { + context.setTransform(1, 0, 0, 1, 0, 0); + } } ++i; } else if (type == ol.render.canvas.Instruction.END_GEOMETRY) { @@ -454,18 +478,6 @@ ol.render.canvas.ImageReplay = function(pixelRatio, tolerance) { goog.base(this, pixelRatio, tolerance); - /** - * @private - * @type {number|undefined} - */ - this.anchorX_ = undefined; - - /** - * @private - * @type {number|undefined} - */ - this.anchorY_ = undefined; - /** * @private * @type {HTMLCanvasElement|HTMLVideoElement|Image} @@ -478,6 +490,18 @@ ol.render.canvas.ImageReplay = function(pixelRatio, tolerance) { */ this.image_ = null; + /** + * @private + * @type {number|undefined} + */ + this.anchorX_ = undefined; + + /** + * @private + * @type {number|undefined} + */ + this.anchorY_ = undefined; + /** * @private * @type {number|undefined} @@ -488,7 +512,13 @@ ol.render.canvas.ImageReplay = function(pixelRatio, tolerance) { * @private * @type {number|undefined} */ - this.width_ = undefined; + this.rotation_ = undefined; + + /** + * @private + * @type {number|undefined} + */ + this.scale_ = undefined; /** * @private @@ -496,6 +526,12 @@ ol.render.canvas.ImageReplay = function(pixelRatio, tolerance) { */ this.snapToPixel_ = undefined; + /** + * @private + * @type {number|undefined} + */ + this.width_ = undefined; + }; goog.inherits(ol.render.canvas.ImageReplay, ol.render.canvas.Replay); @@ -526,6 +562,8 @@ ol.render.canvas.ImageReplay.prototype.drawPointGeometry = goog.asserts.assert(goog.isDef(this.anchorX_)); goog.asserts.assert(goog.isDef(this.anchorY_)); goog.asserts.assert(goog.isDef(this.height_)); + goog.asserts.assert(goog.isDef(this.rotation_)); + goog.asserts.assert(goog.isDef(this.scale_)); goog.asserts.assert(goog.isDef(this.width_)); ol.extent.extend(this.extent_, pointGeometry.getExtent()); this.beginGeometry(pointGeometry); @@ -535,14 +573,17 @@ ol.render.canvas.ImageReplay.prototype.drawPointGeometry = var myEnd = this.drawCoordinates_( flatCoordinates, 0, flatCoordinates.length, stride); this.instructions.push([ - ol.render.canvas.Instruction.DRAW_IMAGE, myBegin, myEnd, - this.anchorX_, this.anchorY_, this.width_, this.height_, - this.image_, this.snapToPixel_ + ol.render.canvas.Instruction.DRAW_IMAGE, myBegin, myEnd, this.image_, + // Remaining arguments to DRAW_IMAGE are in alphabetical order + this.anchorX_, this.anchorY_, this.height_, this.rotation_, this.scale_, + this.snapToPixel_, this.width_ ]); this.hitDetectionInstructions.push([ ol.render.canvas.Instruction.DRAW_IMAGE, myBegin, myEnd, - this.anchorX_, this.anchorY_, this.width_, this.height_, - this.hitDetectionImage_, this.snapToPixel_ + this.hitDetectionImage_, + // Remaining arguments to DRAW_IMAGE are in alphabetical order + this.anchorX_, this.anchorY_, this.height_, this.rotation_, this.scale_, + this.snapToPixel_, this.width_ ]); this.endGeometry(pointGeometry, data); }; @@ -559,6 +600,8 @@ ol.render.canvas.ImageReplay.prototype.drawMultiPointGeometry = goog.asserts.assert(goog.isDef(this.anchorX_)); goog.asserts.assert(goog.isDef(this.anchorY_)); goog.asserts.assert(goog.isDef(this.height_)); + goog.asserts.assert(goog.isDef(this.rotation_)); + goog.asserts.assert(goog.isDef(this.scale_)); goog.asserts.assert(goog.isDef(this.width_)); ol.extent.extend(this.extent_, multiPointGeometry.getExtent()); this.beginGeometry(multiPointGeometry); @@ -568,14 +611,17 @@ ol.render.canvas.ImageReplay.prototype.drawMultiPointGeometry = var myEnd = this.drawCoordinates_( flatCoordinates, 0, flatCoordinates.length, stride); this.instructions.push([ - ol.render.canvas.Instruction.DRAW_IMAGE, myBegin, myEnd, - this.anchorX_, this.anchorY_, this.width_, this.height_, - this.image_, this.snapToPixel_ + ol.render.canvas.Instruction.DRAW_IMAGE, myBegin, myEnd, this.image_, + // Remaining arguments to DRAW_IMAGE are in alphabetical order + this.anchorX_, this.anchorY_, this.height_, this.rotation_, this.scale_, + this.snapToPixel_, this.width_ ]); this.hitDetectionInstructions.push([ ol.render.canvas.Instruction.DRAW_IMAGE, myBegin, myEnd, - this.anchorX_, this.anchorY_, this.width_, this.height_, - this.hitDetectionImage_, this.snapToPixel_ + this.hitDetectionImage_, + // Remaining arguments to DRAW_IMAGE are in alphabetical order + this.anchorX_, this.anchorY_, this.height_, this.rotation_, this.scale_, + this.snapToPixel_, this.width_ ]); this.endGeometry(multiPointGeometry, data); }; @@ -592,8 +638,10 @@ ol.render.canvas.ImageReplay.prototype.finish = function() { this.hitDetectionImage_ = null; this.image_ = null; this.height_ = undefined; - this.width_ = undefined; + this.scale_ = undefined; + this.rotation_ = undefined; this.snapToPixel_ = undefined; + this.width_ = undefined; }; @@ -615,9 +663,11 @@ ol.render.canvas.ImageReplay.prototype.setImageStyle = function(imageStyle) { this.anchorY_ = anchor[1]; this.hitDetectionImage_ = hitDetectionImage; this.image_ = image; - this.width_ = size[0]; this.height_ = size[1]; + this.rotation_ = imageStyle.getRotation(); + this.scale_ = imageStyle.getScale(); this.snapToPixel_ = imageStyle.getSnapToPixel(); + this.width_ = size[0]; }; diff --git a/src/ol/style/circlestyle.js b/src/ol/style/circlestyle.js index c698f46247..98b2c0a48b 100644 --- a/src/ol/style/circlestyle.js +++ b/src/ol/style/circlestyle.js @@ -52,6 +52,7 @@ ol.style.Circle = function(opt_options) { anchor: [size / 2, size / 2], imageState: ol.style.ImageState.LOADED, rotation: 0, + scale: 1, size: [size, size], snapToPixel: undefined, subtractViewRotation: false diff --git a/src/ol/style/iconstyle.js b/src/ol/style/iconstyle.js index 96b312ee92..c4df241a40 100644 --- a/src/ol/style/iconstyle.js +++ b/src/ol/style/iconstyle.js @@ -83,10 +83,16 @@ ol.style.Icon = function(opt_options) { */ var rotation = goog.isDef(options.rotation) ? options.rotation : 0; + /** + * @type {number} + */ + var scale = goog.isDef(options.scale) ? options.scale : 1; + goog.base(this, { anchor: anchor, imageState: ol.style.ImageState.IDLE, rotation: rotation, + scale: scale, size: size, snapToPixel: undefined, subtractViewRotation: false diff --git a/src/ol/style/imagestyle.js b/src/ol/style/imagestyle.js index ddb5089401..4491030da5 100644 --- a/src/ol/style/imagestyle.js +++ b/src/ol/style/imagestyle.js @@ -22,6 +22,7 @@ ol.style.ImageState = { * @typedef {{anchor: ol.Pixel, * imageState: ol.style.ImageState, * rotation: number, + * scale: number, * size: ol.Size, * snapToPixel: (boolean|undefined), * subtractViewRotation: boolean}} @@ -53,10 +54,16 @@ ol.style.Image = function(options) { /** * @private - * @type {number|undefined} + * @type {number} */ this.rotation_ = options.rotation; + /** + * @private + * @type {number} + */ + this.scale_ = options.scale; + /** * @protected * @type {ol.Size} @@ -104,13 +111,21 @@ ol.style.Image.prototype.getImageState = function() { /** - * @return {number|undefined} Rotation. + * @return {number} Rotation. */ ol.style.Image.prototype.getRotation = function() { return this.rotation_; }; +/** + * @return {number} Scale. + */ +ol.style.Image.prototype.getScale = function() { + return this.scale_; +}; + + /** * @return {ol.Size} Size. */