Style texts and draw them on canvas

This commit is contained in:
GaborFarkas
2017-05-09 18:55:50 +02:00
parent 2ed5abed07
commit da60b96445

View File

@@ -1,47 +1,99 @@
goog.provide('ol.render.webgl.TextReplay');
goog.require('ol');
goog.require('ol.colorlike');
goog.require('ol.has');
goog.require('ol.render.webgl');
goog.require('ol.render.webgl.imagereplay.defaultshader');
goog.require('ol.render.webgl.Replay');
goog.require('ol.webgl');
goog.require('ol.webgl.Context');
if (ol.ENABLE_WEBGL) {
/**
* @constructor
* @abstract
* @extends {ol.render.webgl.Replay}
* @param {number} tolerance Tolerance.
* @param {ol.Extent} maxExtent Max extent.
* @struct
*/
ol.render.webgl.TextReplay = function(tolerance, maxExtent) {};
ol.render.webgl.TextReplay = function(tolerance, maxExtent) {
ol.render.webgl.Replay.call(this, tolerance, maxExtent);
/**
* @param {ol.style.Text} textStyle Text style.
*/
ol.render.webgl.TextReplay.prototype.setTextStyle = function(textStyle) {};
/**
* @private
* @type {ol.render.webgl.imagereplay.defaultshader.Locations}
*/
this.defaultLocations_ = null;
/**
* @private
* @type {Array.<Array.<?>>}
*/
this.styles_ = [];
/**
* @private
* @type {Array.<number>}
*/
this.styleIndices_ = [];
/**
* @private
* @type {Array.<HTMLCanvasElement>}
*/
this.images_ = [];
/**
* @private
* @type {Array.<WebGLTexture>}
*/
this.textures_ = [];
/**
* @private
* @type {{strokeColor: (ol.ColorLike|null),
* lineCap: (string|undefined),
* lineDash: Array.<number>,
* lineDashOffset: (number|undefined),
* lineJoin: (string|undefined),
* lineWidth: (number|undefined),
* miterLimit: (number|undefined),
* fillColor: (ol.ColorLike|null),
* text: string,
* font: (string|undefined),
* textAlign: (string|undefined),
* textBaseline: (string|undefined),
* offsetX: (number|undefined),
* offsetY: (number|undefined),
* scale: (number|undefined),
* rotation: (number|undefined),
* rotateWithView: (boolean|undefined)}
*/
this.state_ = {
strokeColor: null,
lineCap: undefined,
lineDash: null,
lineDashOffset: undefined,
lineJoin: undefined,
lineWidth: undefined,
miterLimit: undefined,
fillColor: null,
text: '',
font: undefined,
textAlign: undefined,
textBaseline: undefined,
offsetX: undefined,
offsetY: undefined,
scale: undefined,
rotation: undefined,
rotateWithView: undefined
};
/**
* @param {ol.webgl.Context} context Context.
* @param {ol.Coordinate} center Center.
* @param {number} resolution Resolution.
* @param {number} rotation Rotation.
* @param {ol.Size} size Size.
* @param {number} pixelRatio Pixel ratio.
* @param {number} opacity Global opacity.
* @param {Object.<string, boolean>} skippedFeaturesHash Ids of features
* to skip.
* @param {function((ol.Feature|ol.render.Feature)): T|undefined} featureCallback Feature callback.
* @param {boolean} oneByOne Draw features one-by-one for the hit-detecion.
* @param {ol.Extent=} opt_hitExtent Hit extent: Only features intersecting
* this extent are checked.
* @return {T|undefined} Callback result.
* @template T
*/
ol.render.webgl.TextReplay.prototype.replay = function(context,
center, resolution, rotation, size, pixelRatio,
opacity, skippedFeaturesHash,
featureCallback, oneByOne, opt_hitExtent) {
return undefined;
};
ol.inherits(ol.render.webgl.TextReplay, ol.render.webgl.Replay);
/**
* @param {Array.<number>} flatCoordinates Flat coordinates.
@@ -52,7 +104,72 @@ if (ol.ENABLE_WEBGL) {
* @param {ol.Feature|ol.render.Feature} feature Feature.
*/
ol.render.webgl.TextReplay.prototype.drawText = function(flatCoordinates, offset,
end, stride, geometry, feature) {};
end, stride, geometry, feature) {
//For now we create one texture per feature. That is, only multiparts are grouped.
//TODO: speed up rendering with SDF, or at least glyph atlases
var state = this.state_;
if (state.text && (state.fillColor || state.strokeColor)) {
this.images_.push(this.createTextImage_());
}
};
/**
* @private
* @return {HTMLCanvasElement} Text image.
*/
ol.render.webgl.TextReplay.prototype.createTextImage_ = function() {
var state = this.state_;
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
ctx.font = state.font;
var lineHeight = Math.round(ctx.measureText('M').width * 1.5 + state.lineWidth * 2);
var lines = state.text.split('\n');
//FIXME: use pixelRatio
var textHeight = Math.ceil(lineHeight * lines.length * state.scale);
var longestLine = lines.map(function(str) {
return ctx.measureText(str).width;
}).reduce(function(max, curr) {
return Math.max(max, curr);
});
//FIXME: use pixelRatio
var textWidth = Math.ceil((longestLine + state.lineWidth * 2) * state.scale);
//Parameterize the canvas
canvas.width = textWidth;
canvas.height = textHeight;
ctx.fillStyle = state.fillColor;
ctx.strokeStyle = state.strokeColor;
ctx.lineWidth = state.lineWidth;
ctx.lineCap = state.lineCap;
ctx.lineJoin = state.lineJoin;
ctx.miterLimit = state.miterLimit;
ctx.textAlign = 'left';
ctx.textBaseline = 'top';
if (ol.has.CANVAS_LINE_DASH) {
//FIXME: use pixelRatio
ctx.setLineDash(state.lineDash);
ctx.lineDashOffset = state.lineDashOffset;
}
if (state.scale !== 1) {
//FIXME: use pixelRatio
ctx.setTransform(state.scale, 0, 0, state.scale, 0, 0);
}
//Draw the text on the canvas
var lineY = 0;
for (var i = 0, ii = lines.length; i < ii; ++i) {
if (state.strokeColor) {
ctx.strokeText(lines[i], 0, lineY);
}
if (state.fillColor) {
ctx.fillText(lines[i], 0, lineY);
}
lineY += lineHeight;
}
return canvas;
};
/**
* @abstract
@@ -61,11 +178,65 @@ if (ol.ENABLE_WEBGL) {
ol.render.webgl.TextReplay.prototype.finish = function(context) {};
/**
* @param {ol.webgl.Context} context WebGL context.
* @return {function()} Delete resources function.
* @inheritDoc
*/
ol.render.webgl.TextReplay.prototype.getDeleteResourcesFunction = function(context) {
return ol.nullFunction;
var verticesBuffer = this.verticesBuffer;
var indicesBuffer = this.indicesBuffer;
var textures = this.textures_;
var gl = context.getGL();
return function() {
if (!gl.isContextLost()) {
var i, ii;
for (i = 0, ii = textures.length; i < ii; ++i) {
gl.deleteTexture(textures[i]);
}
}
context.deleteBuffer(verticesBuffer);
context.deleteBuffer(indicesBuffer);
};
};
/**
* @param {ol.style.Text} textStyle Text style.
*/
ol.render.webgl.TextReplay.prototype.setTextStyle = function(textStyle) {
var state = this.state_;
if (!textStyle) {
state.text = '';
} else {
var textFillStyle = textStyle.getFill();
if (!textFillStyle) {
state.fillColor = null;
} else {
var textFillStyleColor = textFillStyle.getColor();
state.fillColor = ol.colorlike.asColorLike(textFillStyleColor ?
textFillStyleColor : ol.render.webgl.defaultFillStyle);
}
var textStrokeStyle = textStyle.getStroke();
if (!textStrokeStyle) {
state.strokeColor = null;
} else {
var textStrokeStyleColor = textFillStyle.getColor();
state.strokeColor = ol.colorlike.asColorLike(textStrokeStyleColor ?
textStrokeStyleColor : ol.render.webgl.defaultStrokeStyle);
state.lineWidth = textStrokeStyle.getWidth() || ol.render.webgl.defaultLineWidth;
state.lineCap = textStrokeStyle.getLineCap() || ol.render.webgl.defaultLineCap;
state.lineDash = textStrokeStyle.getLineDash() || ol.render.webgl.defaultLineDash;
state.lineDashOffset = textStrokeStyle.getLineDashOffset() || ol.render.webgl.defaultLineDashOffset;
state.lineJoin = textStrokeStyle.getLineJoin() || ol.render.webgl.defaultLineJoin;
state.miterLimit = textStrokeStyle.getMiterLimit() || ol.render.webgl.defaultMiterLimit;
}
state.font = textStyle.getFont() || ol.render.webgl.defaultFont;
state.offsetX = textStyle.getOffsetX() || 0;
state.offsetY = textStyle.getOffsetY() || 0;
state.rotateWithView = !!textStyle.getRotateWithView();
state.rotation = textStyle.getRotation() || 0;
state.scale = textStyle.getScale() || 1;
state.text = textStyle.getText() || '';
state.textAlign = textStyle.getTextAlign() || ol.render.webgl.defaultTextAlign;
state.textBaseline = textStyle.getTextBaseline() || ol.render.webgl.defaultTextBaseline;
}
};
}