Created
September 14, 2019 22:31
-
-
Save postcasio/6e12f94bc1b16bd4fb19d07664ace2ba to your computer and use it in GitHub Desktop.
BitmapFont
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export default class BitmapFont { | |
fileName: string; | |
height: number; | |
constructor(path: string); | |
drawText(surface: Surface, x: number, y: number, text: string, color?: Color, wrap_width?: number): void; | |
getTextSize(text: string, wrap?: number): { | |
width: number; | |
height: number; | |
}; | |
heightOf(text: string, wrap?: number): number; | |
widthOf(text: string): number; | |
wordWrap(text: string, wrapWidth: number): string[]; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var parseBmfontAscii = function parseBMFontAscii(data) { | |
if (!data) | |
throw new Error('no data provided') | |
data = data.toString().trim(); | |
var output = { | |
pages: [], | |
chars: [], | |
kernings: [] | |
}; | |
var lines = data.split(/\r\n?|\n/g); | |
if (lines.length === 0) | |
throw new Error('no data in BMFont file') | |
for (var i = 0; i < lines.length; i++) { | |
var lineData = splitLine(lines[i], i); | |
if (!lineData) //skip empty lines | |
continue | |
if (lineData.key === 'page') { | |
if (typeof lineData.data.id !== 'number') | |
throw new Error('malformed file at line ' + i + ' -- needs page id=N') | |
if (typeof lineData.data.file !== 'string') | |
throw new Error('malformed file at line ' + i + ' -- needs page file="path"') | |
output.pages[lineData.data.id] = lineData.data.file; | |
} else if (lineData.key === 'chars' || lineData.key === 'kernings') ; else if (lineData.key === 'char') { | |
output.chars.push(lineData.data); | |
} else if (lineData.key === 'kerning') { | |
output.kernings.push(lineData.data); | |
} else { | |
output[lineData.key] = lineData.data; | |
} | |
} | |
return output | |
}; | |
function splitLine(line, idx) { | |
line = line.replace(/\t+/g, ' ').trim(); | |
if (!line) | |
return null | |
var space = line.indexOf(' '); | |
if (space === -1) | |
throw new Error("no named row at line " + idx) | |
var key = line.substring(0, space); | |
line = line.substring(space + 1); | |
//clear "letter" field as it is non-standard and | |
//requires additional complexity to parse " / = symbols | |
line = line.replace(/letter=[\'\"]\S+[\'\"]/gi, ''); | |
line = line.split("="); | |
line = line.map(function(str) { | |
return str.trim().match((/(".*?"|[^"\s]+)+(?=\s*|\s*$)/g)) | |
}); | |
var data = []; | |
for (var i = 0; i < line.length; i++) { | |
var dt = line[i]; | |
if (i === 0) { | |
data.push({ | |
key: dt[0], | |
data: "" | |
}); | |
} else if (i === line.length - 1) { | |
data[data.length - 1].data = parseData(dt[0]); | |
} else { | |
data[data.length - 1].data = parseData(dt[0]); | |
data.push({ | |
key: dt[1], | |
data: "" | |
}); | |
} | |
} | |
var out = { | |
key: key, | |
data: {} | |
}; | |
data.forEach(function(v) { | |
out.data[v.key] = v.data; | |
}); | |
return out | |
} | |
function parseData(data) { | |
if (!data || data.length === 0) | |
return "" | |
if (data.indexOf('"') === 0 || data.indexOf("'") === 0) | |
return data.substring(1, data.length - 1) | |
if (data.indexOf(',') !== -1) | |
return parseIntList(data) | |
return parseInt(data, 10) | |
} | |
function parseIntList(data) { | |
return data.split(',').map(function(val) { | |
return parseInt(val, 10) | |
}) | |
} | |
function createTriStripList(w, h, x, y, mask = Color.White) { | |
return new VertexList([ | |
{ x: 0, y: 0, u: x, v: 1 - y, color: mask }, | |
{ x: 1, y: 0, u: x + w, v: 1 - y, color: mask }, | |
{ x: 0, y: 1, u: x, v: 1 - (y + h), color: mask }, | |
{ x: 1, y: 1, u: x + w, v: 1 - (y + h), color: mask } | |
]); | |
} | |
function createShape(texture, w, h, x, y, mask = Color.White) { | |
return new Shape(ShapeType.TriStrip, texture, createTriStripList(w / texture.width, h / texture.height, x / texture.width, y / texture.height, mask)); | |
} | |
class BitmapFont { | |
constructor(path) { | |
this.characters = {}; | |
const data = parseBmfontAscii(FS.readFile(path)); | |
this.fileName = path; | |
const dir = FS.directoryOf(path); | |
this.textures = data.pages.map(page => new Texture(dir + "/" + page)); | |
this.height = data.common.lineHeight; | |
this.base = data.common.base; | |
this.characters = data.chars.reduce((chars, char) => { | |
chars[char.id] = Object.assign(Object.assign({}, char), { texture: this.textures[char.page], shape: createShape(this.textures[char.page], char.width, char.height, char.x, char.y) }); | |
return chars; | |
}, {}); | |
} | |
drawText(surface, x, y, text, color, wrap_width) { | |
let currentX = x; | |
let currentY = y; | |
const transform = new Transform(); | |
for (let i = 0; i < text.length; i++) { | |
let charCode = text.charCodeAt(i); | |
if (charCode === 10) { | |
currentX = x; | |
currentY += this.height; | |
continue; | |
} | |
const char = this.characters[charCode]; | |
if (!char) { | |
continue; | |
} | |
if (char.id >= 33) { | |
transform.identity(); | |
transform.scale(char.width, char.height); | |
transform.translate(currentX + char.xoffset, currentY + char.yoffset + this.height / this.base); | |
char.shape.draw(surface, transform); | |
} | |
currentX += char.xadvance; | |
if (wrap_width && currentX >= wrap_width + x) { | |
currentX = x; | |
currentY += this.height; | |
} | |
} | |
} | |
getTextSize(text, wrap) { | |
let currentX = 0; | |
let currentY = 0; | |
let maxX = 0; | |
for (let i = 0; i < text.length; i++) { | |
let charCode = text.charCodeAt(i); | |
if (charCode === 10) { | |
maxX = Math.max(currentX, maxX); | |
currentX = 0; | |
currentY += this.height; | |
continue; | |
} | |
const char = this.characters[charCode]; | |
if (!char) { | |
continue; | |
} | |
currentX += char.xadvance; | |
if (wrap && currentX >= wrap) { | |
maxX = Math.max(currentX, maxX); | |
currentX = 0; | |
currentY += this.height; | |
} | |
} | |
return { width: Math.max(currentX, maxX), height: currentY + this.height }; | |
} | |
heightOf(text, wrap) { | |
return wrap ? this.getTextSize(text, wrap).height : this.height; | |
} | |
widthOf(text) { | |
return this.getTextSize(text).width; | |
} | |
wordWrap(text, wrapWidth) { | |
let currentX = 0; | |
let currentLine = ""; | |
const lines = []; | |
for (let i = 0; i < text.length; i++) { | |
let charCode = text.charCodeAt(i); | |
if (charCode === 10) { | |
lines.push(currentLine); | |
currentLine = ""; | |
continue; | |
} | |
const char = this.characters[charCode]; | |
if (!char) { | |
continue; | |
} | |
currentLine += text.substr(i, 1); | |
if (currentX >= wrapWidth) { | |
lines.push(currentLine); | |
currentLine = ""; | |
} | |
} | |
lines.push(currentLine); | |
return lines; | |
} | |
} | |
export default BitmapFont; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment