Only split text at line angle changes

This commit is contained in:
Andreas Hocevar
2017-11-02 19:51:47 +01:00
parent ddba26b193
commit 431d570b91
7 changed files with 153 additions and 123 deletions
+41 -23
View File
@@ -142,9 +142,9 @@ ol.render.canvas.Replay = function(tolerance, maxExtent, resolution, pixelRatio,
/**
* @private
* @type {Array.<Array.<number>>}
* @type {!ol.Transform}
*/
this.chars_ = [];
this.resetTransform_ = ol.transform.create();
};
ol.inherits(ol.render.canvas.Replay, ol.render.VectorContext);
@@ -569,34 +569,52 @@ ol.render.canvas.Replay.prototype.replay_ = function(
case ol.render.canvas.Instruction.DRAW_CHARS:
var begin = /** @type {number} */ (instruction[1]);
var end = /** @type {number} */ (instruction[2]);
var images = /** @type {Array.<HTMLCanvasElement>} */ (instruction[3]);
// Remaining arguments in DRAW_CHARS are in alphabetical order
var baseline = /** @type {number} */ (instruction[4]);
declutterGroup = /** @type {ol.DeclutterGroup} */ (instruction[5]);
var exceedLength = /** @type {number} */ (instruction[6]);
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 maxAngle = /** @type {number} */ (instruction[7]);
var measure = /** @type {function(string):number} */ (instruction[8]);
var offsetY = /** @type {number} */ (instruction[9]);
var text = /** @type {string} */ (instruction[10]);
var align = /** @type {number} */ (instruction[11]);
var textScale = /** @type {number} */ (instruction[12]);
var stroke = /** @type {boolean} */ (instruction[10]);
var strokeWidth = /** @type {number} */ (instruction[11]);
var text = /** @type {string} */ (instruction[12]);
var textAlign = /** @type {number} */ (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) * align;
var chars = ol.geom.flat.textpath.lineString(
pixelCoordinates, begin, end, 2, text, measure, startM, maxAngle, this.chars_);
var numChars = text.length;
if (chars) {
var fillHeight = images[images.length - 1].height;
for (var c = 0, cc = images.length; c < cc; ++c) {
var char = chars[c % numChars]; // x, y, rotation
var label = images[c];
anchorX = label.width / 2;
anchorY = baseline * label.height + (0.5 - baseline) * (label.height - fillHeight) - offsetY;
this.replayImage_(context, char[0], char[1], label,
anchorX, anchorY, declutterGroup, label.height, 1, 0, 0, char[2], textScale, false, label.width);
var startM = (pathLength - textLength) * 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) {
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);
anchorX = /** @type {number} */ (part[2]) + strokeWidth;
anchorY = baseline * label.height + (0.5 - baseline) * strokeWidth - offsetY;
this.replayImage_(context,
/** @type {number} */ (part[0]), /** @type {number} */ (part[1]), label,
anchorX, anchorY, declutterGroup, label.height, 1, 0, 0,
/** @type {number} */ (part[3]), textScale, false, label.width);
}
}
if (fill) {
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);
anchorX = /** @type {number} */ (part[2]);
anchorY = baseline * label.height - offsetY;
this.replayImage_(context,
/** @type {number} */ (part[0]), /** @type {number} */ (part[1]), label,
anchorX, anchorY, declutterGroup, label.height, 1, 0, 0,
/** @type {number} */ (part[3]), textScale, false, label.width);
}
}
}
}
+62 -59
View File
@@ -115,6 +115,12 @@ ol.render.canvas.TextReplay = function(
*/
this.strokeKey_ = '';
/**
* @private
* @type {Object.<string, number>}
*/
this.widths_ = {};
while (ol.render.canvas.TextReplay.labelCache_.canExpireCache()) {
ol.render.canvas.TextReplay.labelCache_.pop();
}
@@ -158,19 +164,23 @@ ol.render.canvas.TextReplay.measureTextHeight = (function() {
/**
* @this {Object}
* @param {CanvasRenderingContext2D} context Context.
* @param {number} pixelRatio Pixel ratio.
* @param {string} font Font.
* @param {string} text Text.
* @return {number} Width.
*/
ol.render.canvas.TextReplay.getTextWidth = function(context, pixelRatio, text) {
var width = this[text];
if (!width) {
this[text] = width = context.measureText(text).width;
}
return width * pixelRatio;
};
ol.render.canvas.TextReplay.measureTextWidth = (function() {
var measureContext;
var currentFont;
return function(font, text) {
if (!measureContext) {
measureContext = ol.dom.createCanvasContext2D(1, 1);
}
if (font != currentFont) {
currentFont = measureContext.font = font;
}
return measureContext.measureText(text).width;
};
})();
/**
@@ -180,24 +190,17 @@ ol.render.canvas.TextReplay.getTextWidth = function(context, pixelRatio, text) {
* each line.
* @return {number} Width of the whole text.
*/
ol.render.canvas.TextReplay.measureTextWidths = (function() {
var context;
return function(font, lines, widths) {
if (!context) {
context = ol.dom.createCanvasContext2D(1, 1);
}
context.font = font;
var numLines = lines.length;
var width = 0;
var currentWidth, i;
for (i = 0; i < numLines; ++i) {
currentWidth = context.measureText(lines[i]).width;
width = Math.max(width, currentWidth);
widths.push(currentWidth);
}
return width;
};
})();
ol.render.canvas.TextReplay.measureTextWidths = function(font, lines, widths) {
var numLines = lines.length;
var width = 0;
var currentWidth, i;
for (i = 0; i < numLines; ++i) {
currentWidth = ol.render.canvas.TextReplay.measureTextWidth(font, lines[i]);
width = Math.max(width, currentWidth);
widths.push(currentWidth);
}
return width;
};
/**
@@ -263,7 +266,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.textFillState_, !!this.textStrokeState_);
var width = label.width / this.pixelRatio;
switch (geometryType) {
case ol.geom.GeometryType.POINT:
@@ -312,13 +315,12 @@ ol.render.canvas.TextReplay.prototype.drawText = function(geometry, feature) {
/**
* @private
* @param {string} text Text.
* @param {boolean} fill Fill.
* @param {boolean} stroke Stroke.
* @return {HTMLCanvasElement} Image.
*/
ol.render.canvas.TextReplay.prototype.getImage_ = function(text, fill, stroke) {
ol.render.canvas.TextReplay.prototype.getImage = function(text, fill, stroke) {
var label;
var key = (stroke ? this.strokeKey_ : '') + this.textKey_ + text + (fill ? this.fillKey_ : '');
@@ -343,7 +345,9 @@ ol.render.canvas.TextReplay.prototype.getImage_ = function(text, fill, stroke) {
Math.ceil((height + strokeWidth) * scale));
label = context.canvas;
ol.render.canvas.TextReplay.labelCache_.set(key, label);
context.scale(scale, scale);
if (scale != 1) {
context.scale(scale, scale);
}
context.font = textState.font;
if (stroke) {
context.strokeStyle = strokeState.strokeStyle;
@@ -422,37 +426,36 @@ ol.render.canvas.TextReplay.prototype.drawChars_ = function(begin, end, declutte
var textState = this.textState_;
var baseline = ol.render.replay.TEXT_ALIGN[textState.textBaseline];
var labels = [];
var text = this.text_;
var numChars = this.text_.length;
var i;
if (stroke) {
for (i = 0; i < numChars; ++i) {
labels.push(this.getImage_(text.charAt(i), false, stroke));
}
}
if (fill) {
for (i = 0; i < numChars; ++i) {
labels.push(this.getImage_(text.charAt(i), fill, false));
}
}
var context = labels[0].getContext('2d');
var offsetY = this.textOffsetY_ * pixelRatio;
var align = ol.render.replay.TEXT_ALIGN[textState.textAlign || ol.render.canvas.defaultTextAlign];
var widths = {};
var textAlign = ol.render.replay.TEXT_ALIGN[textState.textAlign || ol.render.canvas.defaultTextAlign];
var text = this.text_;
var font = textState.font;
var textScale = this.textScale_;
var strokeWidth = strokeState ? strokeState.lineWidth * textScale / 2 : 0;
var widths = this.widths_;
this.instructions.push([ol.render.canvas.Instruction.DRAW_CHARS,
begin, end, labels, baseline, declutterGroup,
textState.exceedLength, textState.maxAngle,
ol.render.canvas.TextReplay.getTextWidth.bind(widths, context, pixelRatio * this.textScale_),
offsetY, this.text_, align, 1
begin, end, baseline, declutterGroup,
textState.exceedLength, fill, textState.maxAngle,
function(text) {
var width = widths[text];
if (!width) {
width = widths[text] = ol.render.canvas.TextReplay.measureTextWidth(font, text);
}
return width * textScale * pixelRatio;
},
offsetY, stroke, strokeWidth * pixelRatio, text, textAlign, 1
]);
this.hitDetectionInstructions.push([ol.render.canvas.Instruction.DRAW_CHARS,
begin, end, labels, baseline, declutterGroup,
textState.exceedLength, textState.maxAngle,
ol.render.canvas.TextReplay.getTextWidth.bind(widths, context, this.textScale_),
offsetY, this.text_, align, 1 / pixelRatio
begin, end, baseline, declutterGroup,
textState.exceedLength, fill, textState.maxAngle,
function(text) {
var width = widths[text];
if (!width) {
width = widths[text] = ol.render.canvas.TextReplay.measureTextWidth(font, text);
}
return width * textScale;
},
offsetY, stroke, strokeWidth, text, textAlign, 1 / pixelRatio
]);
};