Always calculate includeChars

This commit is contained in:
Richard Davey 2020-07-30 22:37:34 +01:00
parent 1579069c50
commit a678f723c6

View file

@ -1,489 +1,483 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2020 Photon Storm Ltd.
* @license {@link https://opensource.org/licenses/MIT|MIT License}
*/
/**
* Calculate the full bounds, in local and world space, of a BitmapText Game Object.
*
* Returns a BitmapTextSize object that contains global and local variants of the Game Objects x and y coordinates and
* its width and height. Also includes an array of the line lengths and all word positions.
*
* The global position and size take into account the Game Object's position and scale.
*
* The local position and size just takes into account the font data.
*
* @function GetBitmapTextSize
* @since 3.0.0
* @private
*
* @param {(Phaser.GameObjects.DynamicBitmapText|Phaser.GameObjects.BitmapText)} src - The BitmapText to calculate the position, width, height and bounds values for.
* @param {boolean} [round] - Whether to round the results to the nearest integer.
* @param {boolean} [includeChars] - Should the results include the locations of each character in the `characters` array?
* @param {object} [out] - Optional object to store the results in, to save constant object creation.
*
* @return {Phaser.Types.GameObjects.BitmapText.BitmapTextSize} The calculated bounds values of BitmapText.
*/
var GetBitmapTextSize = function (src, round, includeChars, out)
{
if (out === undefined)
{
out = {
local: {
x: 0,
y: 0,
width: 0,
height: 0
},
global: {
x: 0,
y: 0,
width: 0,
height: 0
},
lines: {
shortest: 0,
longest: 0,
lengths: null,
height: 0
},
wrappedText: '',
words: [],
characters: [],
scaleX: 0,
scaleY: 0
};
return out;
}
var text = src.text;
var textLength = text.length;
var maxWidth = src.maxWidth;
var wordWrapCharCode = src.wordWrapCharCode;
var bx = Number.MAX_VALUE;
var by = Number.MAX_VALUE;
var bw = 0;
var bh = 0;
var chars = src.fontData.chars;
var lineHeight = src.fontData.lineHeight;
var letterSpacing = src.letterSpacing;
var xAdvance = 0;
var yAdvance = 0;
var charCode = 0;
var glyph = null;
var align = src._align;
var x = 0;
var y = 0;
var scale = (src.fontSize / src.fontData.size);
var sx = scale * src.scaleX;
var sy = scale * src.scaleY;
var lastGlyph = null;
var lastCharCode = 0;
var lineWidths = [];
var shortestLine = Number.MAX_VALUE;
var longestLine = 0;
var currentLine = 0;
var currentLineWidth = 0;
var i;
var words = [];
var characters = [];
var current = null;
// Scan for breach of maxWidth and insert carriage-returns
if (maxWidth > 0)
{
for (i = 0; i < textLength; i++)
{
charCode = text.charCodeAt(i);
if (charCode === 10)
{
if (current !== null)
{
words.push({
word: current.word,
i: current.i,
x: current.x * sx,
y: current.y * sy,
w: current.w * sx,
h: current.h * sy,
cr: true
});
current = null;
}
xAdvance = 0;
yAdvance += lineHeight;
lastGlyph = null;
continue;
}
glyph = chars[charCode];
if (!glyph)
{
continue;
}
if (lastGlyph !== null)
{
var glyphKerningOffset = glyph.kerning[lastCharCode];
}
if (charCode === wordWrapCharCode)
{
if (current !== null)
{
words.push({
word: current.word,
i: current.i,
x: current.x * sx,
y: current.y * sy,
w: current.w * sx,
h: current.h * sy,
cr: false
});
current = null;
}
}
else
{
if (current === null)
{
// We're starting a new word, recording the starting index, etc
current = { word: '', i: i, x: xAdvance, y: yAdvance, w: 0, h: lineHeight, cr: false };
}
current.word = current.word.concat(text[i]);
current.w += glyph.xOffset + glyph.xAdvance + ((glyphKerningOffset !== undefined) ? glyphKerningOffset : 0);
}
xAdvance += glyph.xAdvance + letterSpacing;
lastGlyph = glyph;
lastCharCode = charCode;
}
// Last word
if (current !== null)
{
words.push({
word: current.word,
i: current.i,
x: current.x * sx,
y: current.y * sy,
w: current.w * sx,
h: current.h * sy,
cr: false
});
}
// Reset for the next loop
xAdvance = 0;
yAdvance = 0;
lastGlyph = null;
lastCharCode = 0;
// Loop through the words array and see if we've got any > maxWidth
var prev;
var offset = 0;
var crs = [];
for (i = 0; i < words.length; i++)
{
var entry = words[i];
var left = entry.x;
var right = entry.x + entry.w;
if (prev)
{
var diff = left - (prev.x + prev.w);
offset = left - (diff + prev.w);
prev = null;
}
var checkLeft = left - offset;
var checkRight = right - offset;
if (checkLeft > maxWidth || checkRight > maxWidth)
{
crs.push(entry.i - 1);
if (entry.cr)
{
crs.push(entry.i + entry.word.length);
offset = 0;
prev = null;
}
else
{
prev = entry;
}
}
else if (entry.cr)
{
crs.push(entry.i + entry.word.length);
offset = 0;
prev = null;
}
}
var stringInsert = function (str, index, value)
{
return str.substr(0, index) + value + str.substr(index + 1);
};
for (i = crs.length - 1; i >= 0; i--)
{
// eslint-disable-next-line quotes
text = stringInsert(text, crs[i], "\n");
}
out.wrappedText = text;
textLength = text.length;
// Recalculated in the next loop
words = [];
current = null;
}
for (i = 0; i < textLength; i++)
{
charCode = text.charCodeAt(i);
if (charCode === 10)
{
if (current !== null)
{
words.push({
word: current.word,
i: current.i,
x: current.x * sx,
y: current.y * sy,
w: current.w * sx,
h: current.h * sy
});
current = null;
}
xAdvance = 0;
yAdvance += lineHeight;
lastGlyph = null;
lineWidths[currentLine] = currentLineWidth;
if (currentLineWidth > longestLine)
{
longestLine = currentLineWidth;
}
if (currentLineWidth < shortestLine)
{
shortestLine = currentLineWidth;
}
currentLine++;
currentLineWidth = 0;
continue;
}
glyph = chars[charCode];
if (!glyph)
{
continue;
}
x = xAdvance;
y = yAdvance;
if (lastGlyph !== null)
{
var kerningOffset = glyph.kerning[lastCharCode];
x += (kerningOffset !== undefined) ? kerningOffset : 0;
}
if (bx > x)
{
bx = x;
}
if (by > y)
{
by = y;
}
var gw = x + glyph.xAdvance;
var gh = y + lineHeight;
if (bw < gw)
{
bw = gw;
}
if (bh < gh)
{
bh = gh;
}
var charWidth = glyph.xOffset + glyph.xAdvance + ((kerningOffset !== undefined) ? kerningOffset : 0);
if (charCode === wordWrapCharCode)
{
if (current !== null)
{
words.push({
word: current.word,
i: current.i,
x: current.x * sx,
y: current.y * sy,
w: current.w * sx,
h: current.h * sy
});
current = null;
}
}
else
{
if (current === null)
{
// We're starting a new word, recording the starting index, etc
current = { word: '', i: i, x: xAdvance, y: yAdvance, w: 0, h: lineHeight };
}
current.word = current.word.concat(text[i]);
current.w += charWidth;
}
if (includeChars)
{
characters.push({
char: text[i],
code: charCode,
x: (glyph.xOffset + xAdvance) * sx,
y: (glyph.yOffset + yAdvance) * sy,
w: glyph.width * sx,
h: glyph.height * sy,
line: currentLine,
glyph: glyph
});
}
xAdvance += glyph.xAdvance + letterSpacing;
lastGlyph = glyph;
lastCharCode = charCode;
currentLineWidth = gw * scale;
}
// Last word
if (current !== null)
{
words.push({
word: current.word,
i: current.i,
x: current.x * sx,
y: current.y * sy,
w: current.w * sx,
h: current.h * sy
});
}
lineWidths[currentLine] = currentLineWidth;
if (currentLineWidth > longestLine)
{
longestLine = currentLineWidth;
}
if (currentLineWidth < shortestLine)
{
shortestLine = currentLineWidth;
}
// Adjust all of the character positions based on alignment
if (align > 0)
{
for (var c = 0; c < characters.length; c++)
{
var currentChar = characters[c];
if (align === 1)
{
currentChar.x += ((longestLine - lineWidths[currentChar.line]) / 2);
}
else if (align === 2)
{
currentChar.x += (longestLine - lineWidths[currentChar.line]);
}
}
}
var local = out.local;
var global = out.global;
var lines = out.lines;
local.x = bx * scale;
local.y = by * scale;
local.width = bw * scale;
local.height = bh * scale;
global.x = (src.x - src.displayOriginX) + (bx * sx);
global.y = (src.y - src.displayOriginY) + (by * sy);
global.width = bw * sx;
global.height = bh * sy;
lines.shortest = shortestLine;
lines.longest = longestLine;
lines.lengths = lineWidths;
if (round)
{
local.x = Math.ceil(local.x);
local.y = Math.ceil(local.y);
local.width = Math.ceil(local.width);
local.height = Math.ceil(local.height);
global.x = Math.ceil(global.x);
global.y = Math.ceil(global.y);
global.width = Math.ceil(global.width);
global.height = Math.ceil(global.height);
lines.shortest = Math.ceil(shortestLine);
lines.longest = Math.ceil(longestLine);
}
// console.log(round, local);
out.words = words;
out.characters = characters;
out.lines.height = lineHeight;
out.scaleX = src.scaleX;
out.scaleY = src.scaleY;
return out;
};
module.exports = GetBitmapTextSize;
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2020 Photon Storm Ltd.
* @license {@link https://opensource.org/licenses/MIT|MIT License}
*/
/**
* Calculate the full bounds, in local and world space, of a BitmapText Game Object.
*
* Returns a BitmapTextSize object that contains global and local variants of the Game Objects x and y coordinates and
* its width and height. Also includes an array of the line lengths and all word positions.
*
* The global position and size take into account the Game Object's position and scale.
*
* The local position and size just takes into account the font data.
*
* @function GetBitmapTextSize
* @since 3.0.0
* @private
*
* @param {(Phaser.GameObjects.DynamicBitmapText|Phaser.GameObjects.BitmapText)} src - The BitmapText to calculate the bounds values for.
* @param {boolean} [round] - Whether to round the positions to the nearest integer.
* @param {object} [out] - Object to store the results in, to save constant object creation. If not provided an empty object is returned.
*
* @return {Phaser.Types.GameObjects.BitmapText.BitmapTextSize} The calculated bounds values of the BitmapText.
*/
var GetBitmapTextSize = function (src, round, out)
{
if (out === undefined)
{
out = {
local: {
x: 0,
y: 0,
width: 0,
height: 0
},
global: {
x: 0,
y: 0,
width: 0,
height: 0
},
lines: {
shortest: 0,
longest: 0,
lengths: null,
height: 0
},
wrappedText: '',
words: [],
characters: [],
scaleX: 0,
scaleY: 0
};
return out;
}
var text = src.text;
var textLength = text.length;
var maxWidth = src.maxWidth;
var wordWrapCharCode = src.wordWrapCharCode;
var bx = Number.MAX_VALUE;
var by = Number.MAX_VALUE;
var bw = 0;
var bh = 0;
var chars = src.fontData.chars;
var lineHeight = src.fontData.lineHeight;
var letterSpacing = src.letterSpacing;
var xAdvance = 0;
var yAdvance = 0;
var charCode = 0;
var glyph = null;
var align = src._align;
var x = 0;
var y = 0;
var scale = (src.fontSize / src.fontData.size);
var sx = scale * src.scaleX;
var sy = scale * src.scaleY;
var lastGlyph = null;
var lastCharCode = 0;
var lineWidths = [];
var shortestLine = Number.MAX_VALUE;
var longestLine = 0;
var currentLine = 0;
var currentLineWidth = 0;
var i;
var words = [];
var characters = [];
var current = null;
// Scan for breach of maxWidth and insert carriage-returns
if (maxWidth > 0)
{
for (i = 0; i < textLength; i++)
{
charCode = text.charCodeAt(i);
if (charCode === 10)
{
if (current !== null)
{
words.push({
word: current.word,
i: current.i,
x: current.x * sx,
y: current.y * sy,
w: current.w * sx,
h: current.h * sy,
cr: true
});
current = null;
}
xAdvance = 0;
yAdvance += lineHeight;
lastGlyph = null;
continue;
}
glyph = chars[charCode];
if (!glyph)
{
continue;
}
if (lastGlyph !== null)
{
var glyphKerningOffset = glyph.kerning[lastCharCode];
}
if (charCode === wordWrapCharCode)
{
if (current !== null)
{
words.push({
word: current.word,
i: current.i,
x: current.x * sx,
y: current.y * sy,
w: current.w * sx,
h: current.h * sy,
cr: false
});
current = null;
}
}
else
{
if (current === null)
{
// We're starting a new word, recording the starting index, etc
current = { word: '', i: i, x: xAdvance, y: yAdvance, w: 0, h: lineHeight, cr: false };
}
current.word = current.word.concat(text[i]);
current.w += glyph.xOffset + glyph.xAdvance + ((glyphKerningOffset !== undefined) ? glyphKerningOffset : 0);
}
xAdvance += glyph.xAdvance + letterSpacing;
lastGlyph = glyph;
lastCharCode = charCode;
}
// Last word
if (current !== null)
{
words.push({
word: current.word,
i: current.i,
x: current.x * sx,
y: current.y * sy,
w: current.w * sx,
h: current.h * sy,
cr: false
});
}
// Reset for the next loop
xAdvance = 0;
yAdvance = 0;
lastGlyph = null;
lastCharCode = 0;
// Loop through the words array and see if we've got any > maxWidth
var prev;
var offset = 0;
var crs = [];
for (i = 0; i < words.length; i++)
{
var entry = words[i];
var left = entry.x;
var right = entry.x + entry.w;
if (prev)
{
var diff = left - (prev.x + prev.w);
offset = left - (diff + prev.w);
prev = null;
}
var checkLeft = left - offset;
var checkRight = right - offset;
if (checkLeft > maxWidth || checkRight > maxWidth)
{
crs.push(entry.i - 1);
if (entry.cr)
{
crs.push(entry.i + entry.word.length);
offset = 0;
prev = null;
}
else
{
prev = entry;
}
}
else if (entry.cr)
{
crs.push(entry.i + entry.word.length);
offset = 0;
prev = null;
}
}
var stringInsert = function (str, index, value)
{
return str.substr(0, index) + value + str.substr(index + 1);
};
for (i = crs.length - 1; i >= 0; i--)
{
// eslint-disable-next-line quotes
text = stringInsert(text, crs[i], "\n");
}
out.wrappedText = text;
textLength = text.length;
// Recalculated in the next loop
words = [];
current = null;
}
for (i = 0; i < textLength; i++)
{
charCode = text.charCodeAt(i);
if (charCode === 10)
{
if (current !== null)
{
words.push({
word: current.word,
i: current.i,
x: current.x * sx,
y: current.y * sy,
w: current.w * sx,
h: current.h * sy
});
current = null;
}
xAdvance = 0;
yAdvance += lineHeight;
lastGlyph = null;
lineWidths[currentLine] = currentLineWidth;
if (currentLineWidth > longestLine)
{
longestLine = currentLineWidth;
}
if (currentLineWidth < shortestLine)
{
shortestLine = currentLineWidth;
}
currentLine++;
currentLineWidth = 0;
continue;
}
glyph = chars[charCode];
if (!glyph)
{
continue;
}
x = xAdvance;
y = yAdvance;
if (lastGlyph !== null)
{
var kerningOffset = glyph.kerning[lastCharCode];
x += (kerningOffset !== undefined) ? kerningOffset : 0;
}
if (bx > x)
{
bx = x;
}
if (by > y)
{
by = y;
}
var gw = x + glyph.xAdvance;
var gh = y + lineHeight;
if (bw < gw)
{
bw = gw;
}
if (bh < gh)
{
bh = gh;
}
var charWidth = glyph.xOffset + glyph.xAdvance + ((kerningOffset !== undefined) ? kerningOffset : 0);
if (charCode === wordWrapCharCode)
{
if (current !== null)
{
words.push({
word: current.word,
i: current.i,
x: current.x * sx,
y: current.y * sy,
w: current.w * sx,
h: current.h * sy
});
current = null;
}
}
else
{
if (current === null)
{
// We're starting a new word, recording the starting index, etc
current = { word: '', i: i, x: xAdvance, y: yAdvance, w: 0, h: lineHeight };
}
current.word = current.word.concat(text[i]);
current.w += charWidth;
}
characters.push({
char: text[i],
code: charCode,
x: (glyph.xOffset + xAdvance) * sx,
y: (glyph.yOffset + yAdvance) * sy,
w: glyph.width * sx,
h: glyph.height * sy,
line: currentLine,
glyph: glyph
});
xAdvance += glyph.xAdvance + letterSpacing;
lastGlyph = glyph;
lastCharCode = charCode;
currentLineWidth = gw * scale;
}
// Last word
if (current !== null)
{
words.push({
word: current.word,
i: current.i,
x: current.x * sx,
y: current.y * sy,
w: current.w * sx,
h: current.h * sy
});
}
lineWidths[currentLine] = currentLineWidth;
if (currentLineWidth > longestLine)
{
longestLine = currentLineWidth;
}
if (currentLineWidth < shortestLine)
{
shortestLine = currentLineWidth;
}
// Adjust all of the character positions based on alignment
if (align > 0)
{
for (var c = 0; c < characters.length; c++)
{
var currentChar = characters[c];
if (align === 1)
{
currentChar.x += ((longestLine - lineWidths[currentChar.line]) / 2);
}
else if (align === 2)
{
currentChar.x += (longestLine - lineWidths[currentChar.line]);
}
}
}
var local = out.local;
var global = out.global;
var lines = out.lines;
local.x = bx * scale;
local.y = by * scale;
local.width = bw * scale;
local.height = bh * scale;
global.x = (src.x - src.displayOriginX) + (bx * sx);
global.y = (src.y - src.displayOriginY) + (by * sy);
global.width = bw * sx;
global.height = bh * sy;
lines.shortest = shortestLine;
lines.longest = longestLine;
lines.lengths = lineWidths;
if (round)
{
local.x = Math.ceil(local.x);
local.y = Math.ceil(local.y);
local.width = Math.ceil(local.width);
local.height = Math.ceil(local.height);
global.x = Math.ceil(global.x);
global.y = Math.ceil(global.y);
global.width = Math.ceil(global.width);
global.height = Math.ceil(global.height);
lines.shortest = Math.ceil(shortestLine);
lines.longest = Math.ceil(longestLine);
}
out.words = words;
out.characters = characters;
out.lines.height = lineHeight;
out.scaleX = src.scaleX;
out.scaleY = src.scaleY;
return out;
};
module.exports = GetBitmapTextSize;