Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rewrite measureText in writeTextToCanvas #9765

Merged
merged 8 commits into from
Aug 30, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 102 additions & 15 deletions Source/Core/writeTextToCanvas.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,100 @@
import measureText from "../ThirdParty/measureText.js";
import Color from "./Color.js";
import defaultValue from "./defaultValue.js";
import defined from "./defined.js";
import DeveloperError from "./DeveloperError.js";

function measureText(context2D, textString, font, stroke, fill) {
var metrics = context2D.measureText(textString);
ebogo1 marked this conversation as resolved.
Show resolved Hide resolved
var isSpace = !/\S/.test(textString);

if (!isSpace) {
var fontSize = document.defaultView
.getComputedStyle(context2D.canvas)
.getPropertyValue("font-size")
.replace("px", "");
var canvas = document.createElement("canvas");
var padding = 100;
var width = (metrics.width + padding) | 0;
var height = 3 * fontSize;
var baseline = height / 2;
canvas.width = width;
canvas.height = height;

var ctx = canvas.getContext("2d");
ctx.font = font;
ctx.fillStyle = "white";
ctx.fillRect(0, 0, canvas.width + 1, canvas.height + 1);

if (stroke) {
ctx.strokeStyle = "black";
ctx.lineWidth = context2D.lineWidth;
ctx.strokeText(textString, padding / 2, baseline);
}

if (fill) {
ctx.fillStyle = "black";
ctx.fillText(textString, padding / 2, baseline);
}

// Context image data has width * height * 4 elements, because
// each pixel's R, G, B and A are consecutive values in the array.
var pixelData = ctx.getImageData(0, 0, width, height).data;
var length = pixelData.length;
var width4 = width * 4;
var i, j;

var ascent, descent;
// Find the number of rows (from the top) until the first non-white pixel
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Had to expand these because eslint doesn't like empty code blocks in loops..

for (i = 0; i < length; ++i) {
if (pixelData[i] !== 255) {
ascent = (i / width4) | 0;
break;
}
}

// Find the number of rows (from the bottom) until the first non-white pixel
for (i = length - 1; i >= 0; --i) {
if (pixelData[i] !== 255) {
descent = (i / width4) | 0;
break;
}
}

var minx = -1;
// For each column, for each row, check for first non-white pixel
for (i = 0; i < width && minx === -1; ++i) {
for (j = 0; j < height; ++j) {
var pixelIndex = i * 4 + j * width4;
if (
pixelData[pixelIndex] !== 255 ||
pixelData[pixelIndex + 1] !== 255 ||
pixelData[pixelIndex + 2] !== 255 ||
pixelData[pixelIndex + 3] !== 255
) {
minx = i;
break;
}
}
}

return {
width: metrics.width,
height: descent - ascent,
ascent: baseline - ascent,
descent: descent - baseline,
minx: minx - padding / 2,
};
}

return {
width: 0,
height: 0,
ascent: 0,
descent: 0,
minx: 0,
};
}

var imageSmoothingEnabledName;

/**
Expand Down Expand Up @@ -52,7 +143,6 @@ function writeTextToCanvas(text, options) {
canvas.width = 1;
canvas.height = 1;
canvas.style.font = font;

var context2D = canvas.getContext("2d");

if (!defined(imageSmoothingEnabledName)) {
Expand All @@ -72,32 +162,29 @@ function writeTextToCanvas(text, options) {
context2D.lineWidth = strokeWidth;
context2D[imageSmoothingEnabledName] = false;

// textBaseline needs to be set before the measureText call. It won't work otherwise.
// It's magic.
context2D.textBaseline = defaultValue(options.textBaseline, "bottom");
Comment on lines -75 to -77
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this no longer relevant?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure why it was - the measureText function does not use the context's baseline anywhere. Maybe 9 years ago it worked differently? I didn't notice any changes when I removed this line.


// in order for measureText to calculate style, the canvas has to be
// (temporarily) added to the DOM.
canvas.style.visibility = "hidden";
document.body.appendChild(canvas);

var dimensions = measureText(context2D, text, stroke, fill);
var dimensions = measureText(context2D, text, font, stroke, fill);
// Set canvas.dimensions to be accessed in LabelCollection
canvas.dimensions = dimensions;
ebogo1 marked this conversation as resolved.
Show resolved Hide resolved

document.body.removeChild(canvas);
canvas.style.visibility = "";

//Some characters, such as the letter j, have a non-zero starting position.
//This value is used for kerning later, but we need to take it into account
//now in order to draw the text completely on the canvas
var x = -dimensions.bounds.minx;
// Some characters, such as the letter j, have a non-zero starting position.
// This value is used for kerning later, but we need to take it into account
// now in order to draw the text completely on the canvas
var x = -dimensions.minx;

//Expand the width to include the starting position.
// Expand the width to include the starting position.
var width = Math.ceil(dimensions.width) + x + doublePadding;

//While the height of the letter is correct, we need to adjust
//where we start drawing it so that letters like j and y properly dip
//below the line.
// While the height of the letter is correct, we need to adjust
// where we start drawing it so that letters like j and y properly dip
// below the line.

var height = dimensions.height + doublePadding;
var baseline = height - dimensions.ascent + padding;
Expand Down
8 changes: 3 additions & 5 deletions Source/Scene/LabelCollection.js
Original file line number Diff line number Diff line change
Expand Up @@ -387,9 +387,9 @@ function repositionAllGlyphs(label) {
maxGlyphDescent = Math.max(maxGlyphDescent, dimensions.descent);

//Computing the line width must also account for the kerning that occurs between letters.
lastLineWidth += dimensions.width - dimensions.bounds.minx;
lastLineWidth += dimensions.width - dimensions.minx;
if (glyphIndex < glyphLength - 1) {
lastLineWidth += glyphs[glyphIndex + 1].dimensions.bounds.minx;
lastLineWidth += glyphs[glyphIndex + 1].dimensions.minx;
}
maxLineWidth = Math.max(maxLineWidth, lastLineWidth);
}
Expand Down Expand Up @@ -481,9 +481,7 @@ function repositionAllGlyphs(label) {
if (glyphIndex < glyphLength - 1) {
var nextGlyph = glyphs[glyphIndex + 1];
glyphPixelOffset.x +=
(dimensions.width -
dimensions.bounds.minx +
nextGlyph.dimensions.bounds.minx) *
(dimensions.width - dimensions.minx + nextGlyph.dimensions.minx) *
scale;
}
}
Expand Down
207 changes: 0 additions & 207 deletions Source/ThirdParty/measureText.js

This file was deleted.

1 change: 0 additions & 1 deletion gulpfile.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,6 @@ const filesToLeaveInThirdParty = [
"!Source/ThirdParty/*.wasm",
"!Source/ThirdParty/google-earth-dbroot-parser.js",
"!Source/ThirdParty/knockout*.js",
"!Source/ThirdParty/measureText.js",
"!Source/ThirdParty/Uri.js",
];

Expand Down