diff --git a/src/ol/render/canvas/replay.js b/src/ol/render/canvas/replay.js index 0a78d62b73..89e8dd84fc 100644 --- a/src/ol/render/canvas/replay.js +++ b/src/ol/render/canvas/replay.js @@ -15,6 +15,7 @@ goog.require('ol.obj'); goog.require('ol.render.VectorContext'); goog.require('ol.render.canvas'); goog.require('ol.render.canvas.Instruction'); +goog.require('ol.render.replay'); goog.require('ol.transform'); @@ -691,29 +692,30 @@ ol.render.canvas.Replay.prototype.replay_ = function( var baseline = /** @type {number} */ (instruction[3]); declutterGroup = /** @type {ol.DeclutterGroup} */ (instruction[4]); var exceedLength = /** @type {number} */ (instruction[5]); - var fill = /** @type {boolean} */ (instruction[6]); + var fillKey = /** @type {string} */ (instruction[6]); var maxAngle = /** @type {number} */ (instruction[7]); var measure = /** @type {function(string):number} */ (instruction[8]); var offsetY = /** @type {number} */ (instruction[9]); - var stroke = /** @type {boolean} */ (instruction[10]); + var strokeKey = /** @type {string} */ (instruction[10]); var strokeWidth = /** @type {number} */ (instruction[11]); var text = /** @type {string} */ (instruction[12]); - var textAlign = /** @type {number} */ (instruction[13]); + var textKey = /** @type {string} */ (instruction[13]); var textScale = /** @type {number} */ (instruction[14]); var pathLength = ol.geom.flat.length.lineString(pixelCoordinates, begin, end, 2); var textLength = measure(text); if (exceedLength || textLength <= pathLength) { - var startM = (pathLength - textLength) * textAlign; + var textAlign = /** @type {ol.render.canvas.TextReplay} */ (this).textStates[textKey].textAlign; + var startM = (pathLength - textLength) * ol.render.replay.TEXT_ALIGN[textAlign]; var parts = ol.geom.flat.textpath.lineString( pixelCoordinates, begin, end, 2, text, measure, startM, maxAngle); if (parts) { var c, cc, chars, label, part; - if (stroke) { + if (strokeKey) { for (c = 0, cc = parts.length; c < cc; ++c) { part = parts[c]; // x, y, anchorX, rotation, chunk chars = /** @type {string} */ (part[4]); - label = /** @type {ol.render.canvas.TextReplay} */ (this).getImage(chars, false, true); + label = /** @type {ol.render.canvas.TextReplay} */ (this).getImage(chars, textKey, '', strokeKey); anchorX = /** @type {number} */ (part[2]) + strokeWidth; anchorY = baseline * label.height + (0.5 - baseline) * 2 * strokeWidth - offsetY; this.replayImage_(context, @@ -723,11 +725,11 @@ ol.render.canvas.Replay.prototype.replay_ = function( ol.render.canvas.defaultPadding, null, null); } } - if (fill) { + if (fillKey) { for (c = 0, cc = parts.length; c < cc; ++c) { part = parts[c]; // x, y, anchorX, rotation, chunk chars = /** @type {string} */ (part[4]); - label = /** @type {ol.render.canvas.TextReplay} */ (this).getImage(chars, true, false); + label = /** @type {ol.render.canvas.TextReplay} */ (this).getImage(chars, textKey, fillKey, ''); anchorX = /** @type {number} */ (part[2]); anchorY = baseline * label.height - offsetY; this.replayImage_(context, diff --git a/src/ol/render/canvas/textreplay.js b/src/ol/render/canvas/textreplay.js index 12a258908e..f5dedfe530 100644 --- a/src/ol/render/canvas/textreplay.js +++ b/src/ol/render/canvas/textreplay.js @@ -78,18 +78,33 @@ ol.render.canvas.TextReplay = function( */ this.textFillState_ = null; + /** + * @type {Object.} + */ + this.fillStates = {}; + /** * @private * @type {?ol.CanvasStrokeState} */ this.textStrokeState_ = null; + /** + * @type {Object.} + */ + this.strokeStates = {}; + /** * @private * @type {ol.CanvasTextState} */ this.textState_ = /** @type {ol.CanvasTextState} */ ({}); + /** + * @type {Object.} + */ + this.textStates = {}; + /** * @private * @type {string} @@ -110,7 +125,7 @@ ol.render.canvas.TextReplay = function( /** * @private - * @type {Object.} + * @type {Object.>} */ this.widths_ = {}; @@ -248,7 +263,7 @@ ol.render.canvas.TextReplay.prototype.drawText = function(geometry, feature) { this.endGeometry(geometry, feature); } else { - var label = this.getImage(this.text_, !!this.textFillState_, !!this.textStrokeState_); + var label = this.getImage(this.text_, this.textKey_, this.fillKey_, this.strokeKey_); var width = label.width / this.pixelRatio; switch (geometryType) { case ol.geom.GeometryType.POINT: @@ -303,23 +318,24 @@ ol.render.canvas.TextReplay.prototype.drawText = function(geometry, feature) { /** * @param {string} text Text. - * @param {boolean} fill Fill. - * @param {boolean} stroke Stroke. + * @param {string} textKey Text style key. + * @param {string} fillKey Fill style key. + * @param {string} strokeKey Stroke style key. * @return {HTMLCanvasElement} Image. */ -ol.render.canvas.TextReplay.prototype.getImage = function(text, fill, stroke) { +ol.render.canvas.TextReplay.prototype.getImage = function(text, textKey, fillKey, strokeKey) { var label; - var key = (stroke ? this.strokeKey_ : '') + this.textKey_ + text + (fill ? this.fillKey_ : ''); + var key = strokeKey + textKey + text + fillKey; var labelCache = ol.render.canvas.labelCache; if (!labelCache.containsKey(key)) { - var strokeState = this.textStrokeState_; - var fillState = this.textFillState_; - var textState = this.textState_; + var strokeState = strokeKey ? this.strokeStates[strokeKey] || this.textStrokeState_ : null; + var fillState = fillKey ? this.fillStates[fillKey] || this.textFillState_ : null; + var textState = this.textStates[textKey] || this.textState_; var pixelRatio = this.pixelRatio; var scale = textState.scale * pixelRatio; var align = ol.render.replay.TEXT_ALIGN[textState.textAlign || ol.render.canvas.defaultTextAlign]; - var strokeWidth = stroke && strokeState.lineWidth ? strokeState.lineWidth : 0; + var strokeWidth = strokeKey && strokeState.lineWidth ? strokeState.lineWidth : 0; var lines = text.split('\n'); var numLines = lines.length; @@ -337,7 +353,7 @@ ol.render.canvas.TextReplay.prototype.getImage = function(text, fill, stroke) { context.scale(scale, scale); } context.font = textState.font; - if (stroke) { + if (strokeKey) { context.strokeStyle = strokeState.strokeStyle; context.lineWidth = strokeWidth * (ol.has.SAFARI ? scale : 1); context.lineCap = strokeState.lineCap; @@ -348,7 +364,7 @@ ol.render.canvas.TextReplay.prototype.getImage = function(text, fill, stroke) { context.lineDashOffset = strokeState.lineDashOffset; } } - if (fill) { + if (fillKey) { context.fillStyle = fillState.fillStyle; } context.textBaseline = 'top'; @@ -356,12 +372,12 @@ ol.render.canvas.TextReplay.prototype.getImage = function(text, fill, stroke) { var leftRight = (0.5 - align); var x = align * label.width / scale + leftRight * strokeWidth; var i; - if (stroke) { + if (strokeKey) { for (i = 0; i < numLines; ++i) { context.strokeText(lines[i], x + leftRight * widths[i], 0.5 * strokeWidth + i * lineHeight); } } - if (fill) { + if (fillKey) { for (i = 0; i < numLines; ++i) { context.fillText(lines[i], x + leftRight * widths[i], 0.5 * strokeWidth + i * lineHeight); } @@ -413,23 +429,56 @@ ol.render.canvas.TextReplay.prototype.drawTextImage_ = function(label, begin, en * @param {ol.DeclutterGroup} declutterGroup Declutter group. */ ol.render.canvas.TextReplay.prototype.drawChars_ = function(begin, end, declutterGroup) { - var pixelRatio = this.pixelRatio; var strokeState = this.textStrokeState_; - var fill = !!this.textFillState_; - var stroke = !!strokeState; var textState = this.textState_; + var fillState = this.textFillState_; + + var strokeKey = this.strokeKey_; + if (strokeState) { + if (!(strokeKey in this.strokeStates)) { + this.strokeStates[strokeKey] = /** @type {ol.CanvasStrokeState} */ ({ + strokeStyle: strokeState.strokeStyle, + lineCap: strokeState.lineCap, + lineDashOffset: strokeState.lineDashOffset, + lineWidth: strokeState.lineWidth, + lineJoin: strokeState.lineJoin, + miterLimit: strokeState.miterLimit, + lineDash: strokeState.lineDash + }); + } + } + var textKey = this.textKey_; + if (!(this.textKey_ in this.textStates)) { + this.textStates[this.textKey_] = /** @type {ol.CanvasTextState} */ ({ + font: textState.font, + textAlign: textState.textAlign || ol.render.canvas.defaultTextAlign, + scale: textState.scale + }); + } + var fillKey = this.fillKey_; + if (fillState) { + if (!(fillKey in this.fillStates)) { + this.fillStates[fillKey] = /** @type {ol.CanvasFillState} */ ({ + fillStyle: fillState.fillStyle + }); + } + } + + var pixelRatio = this.pixelRatio; var baseline = ol.render.replay.TEXT_ALIGN[textState.textBaseline]; var offsetY = this.textOffsetY_ * pixelRatio; - var textAlign = ol.render.replay.TEXT_ALIGN[textState.textAlign || ol.render.canvas.defaultTextAlign]; var text = this.text_; var font = textState.font; var textScale = textState.scale; var strokeWidth = strokeState ? strokeState.lineWidth * textScale / 2 : 0; - var widths = this.widths_; + var widths = this.widths_[font]; + if (!widths) { + this.widths_[font] = widths = {}; + } this.instructions.push([ol.render.canvas.Instruction.DRAW_CHARS, begin, end, baseline, declutterGroup, - textState.exceedLength, fill, textState.maxAngle, + textState.exceedLength, fillKey, textState.maxAngle, function(text) { var width = widths[text]; if (!width) { @@ -437,11 +486,11 @@ ol.render.canvas.TextReplay.prototype.drawChars_ = function(begin, end, declutte } return width * textScale * pixelRatio; }, - offsetY, stroke, strokeWidth * pixelRatio, text, textAlign, 1 + offsetY, strokeKey, strokeWidth * pixelRatio, text, textKey, 1 ]); this.hitDetectionInstructions.push([ol.render.canvas.Instruction.DRAW_CHARS, begin, end, baseline, declutterGroup, - textState.exceedLength, fill, textState.maxAngle, + textState.exceedLength, fillKey, textState.maxAngle, function(text) { var width = widths[text]; if (!width) { @@ -449,7 +498,7 @@ ol.render.canvas.TextReplay.prototype.drawChars_ = function(begin, end, declutte } return width * textScale; }, - offsetY, stroke, strokeWidth, text, textAlign, 1 / pixelRatio + offsetY, strokeKey, strokeWidth, text, textKey, 1 / pixelRatio ]); }; diff --git a/test/rendering/ol/style/expected/text-linestring-nice-multi-font.png b/test/rendering/ol/style/expected/text-linestring-nice-multi-font.png new file mode 100644 index 0000000000..3d97ec922a Binary files /dev/null and b/test/rendering/ol/style/expected/text-linestring-nice-multi-font.png differ diff --git a/test/rendering/ol/style/text.test.js b/test/rendering/ol/style/text.test.js index de834df603..e28f81d07b 100644 --- a/test/rendering/ol/style/text.test.js +++ b/test/rendering/ol/style/text.test.js @@ -304,6 +304,17 @@ describe('ol.rendering.style.Text', function() { expectResemble(map, 'rendering/ol/style/expected/text-linestring-nice.png', 2.8, done); }); + it('uses correct font with different styles', function(done) { + createMap('canvas'); + createLineString(nicePath); + map.getView().setResolution(0.25); + vectorSource.getFeatures()[0].getStyle().getText().setFont('18px monospace'); + vectorSource.getFeatures()[1].getStyle().getText().setFont('italic 38px serif'); + vectorSource.getFeatures()[1].getStyle().getText().setTextBaseline('middle'); + vectorSource.getFeatures()[2].getStyle().getText().setTextBaseline('middle'); + expectResemble(map, 'rendering/ol/style/expected/text-linestring-nice-multi-font.png', 7.54, done); + }); + it('renders text along a linestring with scale != 1', function(done) { createMap('canvas'); createLineString(nicePath, undefined, undefined, undefined, undefined, 2);