font bounds and benchmark
This commit is contained in:
parent
33b8a5a839
commit
cfb00b9b18
7 changed files with 84 additions and 43 deletions
|
@ -331,18 +331,26 @@ proc fillText*(
|
|||
font: Font,
|
||||
text: string,
|
||||
color: SomeColor,
|
||||
transform: Vec2 | Mat3 = vec2(0, 0)
|
||||
transform: Vec2 | Mat3 = vec2(0, 0),
|
||||
bounds = vec2(0, 0)
|
||||
) =
|
||||
for path in font.typeset(text):
|
||||
let typeset = font.typeset(text, bounds)
|
||||
for i in 0 ..< typeset.runes.len:
|
||||
var path = font.getGlyphPath(typeset.runes[i])
|
||||
path.transform(translate(typeset.positions[i]) * scale(vec2(font.scale)))
|
||||
image.fillPath(path, color, transform)
|
||||
|
||||
proc fillText*(
|
||||
mask: Mask,
|
||||
font: Font,
|
||||
text: string,
|
||||
transform: Vec2 | Mat3 = vec2(0, 0)
|
||||
transform: Vec2 | Mat3 = vec2(0, 0),
|
||||
bounds = vec2(0, 0)
|
||||
) =
|
||||
for path in font.typeset(text):
|
||||
let typeset = font.typeset(text, bounds)
|
||||
for i in 0 ..< typeset.runes.len:
|
||||
var path = font.getGlyphPath(typeset.runes[i])
|
||||
path.transform(translate(typeset.positions[i]) * scale(vec2(font.scale)))
|
||||
mask.fillPath(path, transform)
|
||||
|
||||
proc strokeText*(
|
||||
|
@ -351,9 +359,13 @@ proc strokeText*(
|
|||
text: string,
|
||||
color: SomeColor,
|
||||
transform: Vec2 | Mat3 = vec2(0, 0),
|
||||
strokeWidth = 1.0
|
||||
strokeWidth = 1.0,
|
||||
bounds = vec2(0, 0)
|
||||
) =
|
||||
for path in font.typeset(text):
|
||||
let typeset = font.typeset(text, bounds)
|
||||
for i in 0 ..< typeset.runes.len:
|
||||
var path = font.getGlyphPath(typeset.runes[i])
|
||||
path.transform(translate(typeset.positions[i]) * scale(vec2(font.scale)))
|
||||
image.strokePath(path, color, transform, strokeWidth)
|
||||
|
||||
proc strokeText*(
|
||||
|
@ -361,7 +373,11 @@ proc strokeText*(
|
|||
font: Font,
|
||||
text: string,
|
||||
transform: Vec2 | Mat3 = vec2(0, 0),
|
||||
strokeWidth = 1.0
|
||||
strokeWidth = 1.0,
|
||||
bounds = vec2(0, 0)
|
||||
) =
|
||||
for path in font.typeset(text):
|
||||
let typeset = font.typeset(text, bounds)
|
||||
for i in 0 ..< typeset.runes.len:
|
||||
var path = font.getGlyphPath(typeset.runes[i])
|
||||
path.transform(translate(typeset.positions[i]) * scale(vec2(font.scale)))
|
||||
mask.strokePath(path, transform, strokeWidth)
|
||||
|
|
|
@ -10,6 +10,10 @@ type
|
|||
size*: float32 ## Font size in pixels.
|
||||
lineHeight*: float32 ## The line height in pixels or AutoLineHeight for the font's default line height.
|
||||
|
||||
TypesetText* = ref object
|
||||
runes*: seq[Rune]
|
||||
positions*: seq[Vec2]
|
||||
|
||||
HAlignMode* = enum
|
||||
haLeft
|
||||
haCenter
|
||||
|
@ -104,9 +108,12 @@ proc typeset*(
|
|||
hAlign = haLeft,
|
||||
vAlign = vaTop,
|
||||
textCase = tcNormal
|
||||
): seq[Path] =
|
||||
var runes = toRunes(text)
|
||||
runes.convertTextCase(textCase)
|
||||
): TypesetText =
|
||||
result = TypesetText()
|
||||
result.runes = toRunes(text)
|
||||
result.runes.convertTextCase(textCase)
|
||||
|
||||
result.positions.setLen(result.runes.len)
|
||||
|
||||
let lineHeight =
|
||||
if font.lineheight >= 0:
|
||||
|
@ -114,23 +121,22 @@ proc typeset*(
|
|||
else:
|
||||
font.defaultLineHeight
|
||||
|
||||
proc glyphAdvance(runes: seq[Rune], font: Font, i: int): float32 =
|
||||
proc glyphAdvance(runes: seq[Rune], font: Font, i: int): float32 {.inline.} =
|
||||
if i + 1 < runes.len:
|
||||
result += font.getKerningAdjustment(runes[i], runes[i + 1])
|
||||
result += font.getGlyphAdvance(runes[i])
|
||||
result *= font.scale
|
||||
|
||||
var
|
||||
positions = newSeq[Vec2](runes.len)
|
||||
at: Vec2
|
||||
prevCanWrap: int
|
||||
at.y = round(font.ascent * font.scale)
|
||||
at.y += (lineheight - font.defaultLineHeight) / 2
|
||||
for i, rune in runes:
|
||||
for i, rune in result.runes:
|
||||
if rune.canWrap():
|
||||
prevCanWrap = i
|
||||
|
||||
let advance = glyphAdvance(runes, font, i)
|
||||
let advance = glyphAdvance(result.runes, font, i)
|
||||
if bounds.x > 0 and at.x + advance > bounds.x: # Wrap to new line
|
||||
at.x = 0
|
||||
at.y += lineHeight
|
||||
|
@ -138,17 +144,12 @@ proc typeset*(
|
|||
# Go back and wrap glyphs after the wrap index down to the next line
|
||||
if prevCanWrap > 0 and prevCanWrap != i:
|
||||
for j in prevCanWrap + 1 ..< i:
|
||||
positions[j] = at
|
||||
at.x += glyphAdvance(runes, font, j)
|
||||
result.positions[j] = at
|
||||
at.x += glyphAdvance(result.runes, font, j)
|
||||
|
||||
positions[i] = at
|
||||
result.positions[i] = at
|
||||
at.x += advance
|
||||
|
||||
for i, rune in runes:
|
||||
var path = font.getGlyphPath(rune)
|
||||
path.transform(translate(positions[i]) * scale(vec2(font.scale)))
|
||||
result.add(path)
|
||||
|
||||
proc parseOtf*(buf: string): Font =
|
||||
result = Font()
|
||||
result.opentype = parseOpenType(buf)
|
||||
|
|
15
tests/benchmark_fonts.nim
Normal file
15
tests/benchmark_fonts.nim
Normal file
|
@ -0,0 +1,15 @@
|
|||
import benchy, pixie
|
||||
|
||||
const text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis in quam in nulla bibendum luctus. Integer dui lectus, ultricies commodo enim quis, laoreet lacinia erat. Vivamus ultrices maximus risus, non aliquam quam sagittis quis. Ut nec diam vitae tortor interdum ullamcorper in aliquet velit. Ut sed lobortis mi. Nulla venenatis lectus varius justo lacinia, quis sollicitudin nunc ultrices. Donec a suscipit arcu, id egestas neque. Nullam commodo pharetra est. Nullam gravida nibh eget quam venenatis lacinia. Vestibulum et libero arcu. Sed dignissim enim eros. Nullam eleifend luctus erat sed luctus. Nunc tincidunt, mi nec tincidunt tristique, ex nulla lobortis sem, sit amet finibus purus justo non massa."
|
||||
|
||||
let font = readFont("tests/fonts/Roboto-Regular.ttf")
|
||||
font.size = 16
|
||||
|
||||
timeIt "typeset":
|
||||
discard font.typeset(text, vec2(500, 0))
|
||||
|
||||
let image = newImage(500, 300)
|
||||
|
||||
timeIt "rasterize":
|
||||
image.fill(rgba(255, 255, 255, 255))
|
||||
image.fillText(font, text, rgba(0, 0, 0, 255), bounds = image.wh)
|
BIN
tests/fonts/diffs/paragraph.png
Normal file
BIN
tests/fonts/diffs/paragraph.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 81 KiB |
BIN
tests/fonts/masters/paragraph.png
Normal file
BIN
tests/fonts/masters/paragraph.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 31 KiB |
BIN
tests/fonts/rendered/paragraph.png
Normal file
BIN
tests/fonts/rendered/paragraph.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 76 KiB |
|
@ -157,17 +157,14 @@ block:
|
|||
let font = readFont("tests/fonts/Roboto-Regular.ttf")
|
||||
font.size = 24
|
||||
|
||||
let
|
||||
image = newImage(200, 100)
|
||||
layout = font.typeset(
|
||||
"Wrapping text to new line",
|
||||
bounds = vec2(200, 0)
|
||||
)
|
||||
|
||||
let image = newImage(200, 100)
|
||||
image.fill(rgba(255, 255, 255, 255))
|
||||
|
||||
for path in layout:
|
||||
image.fillPath(path, rgba(0, 0, 0, 255))
|
||||
image.fillText(
|
||||
font,
|
||||
"Wrapping text to new line",
|
||||
rgba(0, 0, 0, 255),
|
||||
bounds = vec2(200, 0)
|
||||
)
|
||||
|
||||
doDiff(image, "basic8")
|
||||
|
||||
|
@ -175,16 +172,28 @@ block:
|
|||
let font = readFont("tests/fonts/Roboto-Regular.ttf")
|
||||
font.size = 24
|
||||
|
||||
let
|
||||
image = newImage(200, 100)
|
||||
layout = font.typeset(
|
||||
"Supercalifragilisticexpialidocious",
|
||||
bounds = vec2(200, 0)
|
||||
)
|
||||
|
||||
let image = newImage(200, 100)
|
||||
image.fill(rgba(255, 255, 255, 255))
|
||||
|
||||
for path in layout:
|
||||
image.fillPath(path, rgba(0, 0, 0, 255))
|
||||
image.fillText(
|
||||
font,
|
||||
"Supercalifragilisticexpialidocious",
|
||||
rgba(0, 0, 0, 255),
|
||||
bounds = vec2(200, 0)
|
||||
)
|
||||
|
||||
doDiff(image, "basic9")
|
||||
|
||||
block:
|
||||
let font = readFont("tests/fonts/Roboto-Regular.ttf")
|
||||
font.size = 16
|
||||
|
||||
let image = newImage(500, 300)
|
||||
image.fill(rgba(255, 255, 255, 255))
|
||||
image.fillText(
|
||||
font,
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis in quam in nulla bibendum luctus. Integer dui lectus, ultricies commodo enim quis, laoreet lacinia erat. Vivamus ultrices maximus risus, non aliquam quam sagittis quis. Ut nec diam vitae tortor interdum ullamcorper in aliquet velit. Ut sed lobortis mi. Nulla venenatis lectus varius justo lacinia, quis sollicitudin nunc ultrices. Donec a suscipit arcu, id egestas neque. Nullam commodo pharetra est. Nullam gravida nibh eget quam venenatis lacinia. Vestibulum et libero arcu. Sed dignissim enim eros. Nullam eleifend luctus erat sed luctus. Nunc tincidunt, mi nec tincidunt tristique, ex nulla lobortis sem, sit amet finibus purus justo non massa.",
|
||||
rgba(0, 0, 0, 255),
|
||||
bounds = image.wh
|
||||
)
|
||||
|
||||
doDiff(image, "paragraph")
|
||||
|
|
Loading…
Reference in a new issue