diff --git a/pixie.nimble b/pixie.nimble index ba05a47..62de35b 100644 --- a/pixie.nimble +++ b/pixie.nimble @@ -1,4 +1,4 @@ -version = "4.0.5" +version = "4.1.0" author = "Andre von Houck and Ryan Oldenburg" description = "Full-featured 2d graphics library for Nim." license = "MIT" diff --git a/src/pixie/contexts.nim b/src/pixie/contexts.nim index 85a9f0b..7ba9e4a 100644 --- a/src/pixie/contexts.nim +++ b/src/pixie/contexts.nim @@ -482,7 +482,7 @@ proc measureText*(ctx: Context, text: string): TextMetrics {.raises: [PixieError ## text (such as its width, for example). let font = newFont(ctx) - bounds = typeset(font, text).computeBounds() + bounds = typeset(font, text).layoutBounds() result.width = bounds.x proc getLineDash*(ctx: Context): seq[float32] {.inline, raises: [].} = diff --git a/src/pixie/fonts.nim b/src/pixie/fonts.nim index 4a5bf7e..9c14e6a 100644 --- a/src/pixie/fonts.nim +++ b/src/pixie/fonts.nim @@ -467,7 +467,7 @@ proc typeset*( ## wrap: enable/disable text wrapping typeset(@[newSpan(text, font)], bounds, hAlign, vAlign, wrap) -proc computeBounds*(arrangement: Arrangement): Vec2 {.raises: [].} = +proc layoutBounds*(arrangement: Arrangement): Vec2 {.raises: [].} = ## Computes the width and height of the arrangement in pixels. if arrangement.runes.len > 0: for i in 0 ..< arrangement.runes.len: @@ -481,13 +481,13 @@ proc computeBounds*(arrangement: Arrangement): Vec2 {.raises: [].} = # If the text ends with a new line, we need add another line height. result.y += finalRect.h -proc computeBounds*(font: Font, text: string): Vec2 {.inline, raises: [].} = +proc layoutBounds*(font: Font, text: string): Vec2 {.inline, raises: [].} = ## Computes the width and height of the text in pixels. - font.typeset(text).computeBounds() + font.typeset(text).layoutBounds() -proc computeBounds*(spans: seq[Span]): Vec2 {.inline, raises: [].} = +proc layoutBounds*(spans: seq[Span]): Vec2 {.inline, raises: [].} = ## Computes the width and height of the spans in pixels. - typeset(spans).computeBounds() + typeset(spans).layoutBounds() proc parseOtf*(buf: string): Typeface {.raises: [PixieError].} = result = Typeface() @@ -500,34 +500,27 @@ proc parseSvgFont*(buf: string): Typeface {.raises: [PixieError].} = result = Typeface() result.svgFont = svgfont.parseSvgFont(buf) -proc textUber( - target: Image | Mask, - arrangement: Arrangement, - transform = mat3(), - strokeWidth: float32 = 1.0, - lineCap = ButtCap, - lineJoin = MiterJoin, - miterLimit = defaultMiterLimit, - dashes: seq[float32] = @[], - stroke: static[bool] = false -) = +proc computePaths(arrangement: Arrangement): seq[Path] = + ## Takes an Arrangement and computes Paths for drawing. + ## Returns a seq of paths that match the seq of Spans in the arrangement. + ## If you only have one Span you will only get one Path. var line: int for spanIndex, (start, stop) in arrangement.spans: let + spanPath = newPath() font = arrangement.fonts[spanIndex] underlineThickness = font.typeface.underlineThickness * font.scale underlinePosition = font.typeface.underlinePosition * font.scale strikeoutThickness = font.typeface.strikeoutThickness * font.scale strikeoutPosition = font.typeface.strikeoutPosition * font.scale for runeIndex in start .. stop: - let position = arrangement.positions[runeIndex] - - let path = font.typeface.getGlyphPath(arrangement.runes[runeIndex]) + let + position = arrangement.positions[runeIndex] + path = font.typeface.getGlyphPath(arrangement.runes[runeIndex]) path.transform( translate(position) * scale(vec2(font.scale)) ) - var applyDecoration = true if runeIndex == arrangement.lines[line][1]: inc line @@ -553,22 +546,30 @@ proc textUber( font.typeface.isCCW() ) - when stroke: - when type(target) is Image: - for paint in font.paints: - target.strokePath( - path, - paint, - transform, - strokeWidth, - lineCap, - lineJoin, - miterLimit, - dashes - ) - else: # target is Mask + spanPath.addPath(path) + result.add(spanPath) + +proc textUber( + target: Image | Mask, + arrangement: Arrangement, + transform = mat3(), + strokeWidth: float32 = 1.0, + lineCap = ButtCap, + lineJoin = MiterJoin, + miterLimit = defaultMiterLimit, + dashes: seq[float32] = @[], + stroke: static[bool] = false +) = + let spanPaths = arrangement.computePaths() + for spanIndex in 0 ..< arrangement.spans.len: + let path = spanPaths[spanIndex] + when stroke: + when type(target) is Image: + let font = arrangement.fonts[spanIndex] + for paint in font.paints: target.strokePath( path, + paint, transform, strokeWidth, lineCap, @@ -576,12 +577,33 @@ proc textUber( miterLimit, dashes ) - else: - when type(target) is Image: - for paint in font.paints: - target.fillPath(path, paint, transform) - else: # target is Mask - target.fillPath(path, transform) + else: # target is Mask + target.strokePath( + path, + transform, + strokeWidth, + lineCap, + lineJoin, + miterLimit, + dashes + ) + else: + when type(target) is Image: + let font = arrangement.fonts[spanIndex] + for paint in font.paints: + target.fillPath(path, paint, transform) + else: # target is Mask + target.fillPath(path, transform) + +proc computeBounds*( + arrangement: Arrangement, + transform = mat3() +): Rect = + let fullPath = newPath() + for path in arrangement.computePaths(): + fullPath.addPath(path) + fullPath.transform(transform) + fullPath.computeBounds() proc fillText*( target: Image | Mask, diff --git a/tests/fonts/PinyonScript.ttf b/tests/fonts/PinyonScript.ttf new file mode 100644 index 0000000..1bd9d29 Binary files /dev/null and b/tests/fonts/PinyonScript.ttf differ diff --git a/tests/fonts/diffs/customlineheight.png b/tests/fonts/diffs/customlineheight.png index ccce547..4b1283a 100644 Binary files a/tests/fonts/diffs/customlineheight.png and b/tests/fonts/diffs/customlineheight.png differ diff --git a/tests/fonts/diffs/paragraph1.png b/tests/fonts/diffs/paragraph1.png index fe7fc52..55ddc06 100644 Binary files a/tests/fonts/diffs/paragraph1.png and b/tests/fonts/diffs/paragraph1.png differ diff --git a/tests/fonts/diffs/paragraph1_nokern.png b/tests/fonts/diffs/paragraph1_nokern.png index 9222a66..234d52b 100644 Binary files a/tests/fonts/diffs/paragraph1_nokern.png and b/tests/fonts/diffs/paragraph1_nokern.png differ diff --git a/tests/fonts/diffs/paragraph3_nokern.png b/tests/fonts/diffs/paragraph3_nokern.png index c1941e3..ba40833 100644 Binary files a/tests/fonts/diffs/paragraph3_nokern.png and b/tests/fonts/diffs/paragraph3_nokern.png differ diff --git a/tests/fonts/diffs/paragraph3_nokern_2.png b/tests/fonts/diffs/paragraph3_nokern_2.png index e00dae2..6f15308 100644 Binary files a/tests/fonts/diffs/paragraph3_nokern_2.png and b/tests/fonts/diffs/paragraph3_nokern_2.png differ diff --git a/tests/fonts/diffs/paragraph3_nokern_3.png b/tests/fonts/diffs/paragraph3_nokern_3.png index 2ff2c7e..acce806 100644 Binary files a/tests/fonts/diffs/paragraph3_nokern_3.png and b/tests/fonts/diffs/paragraph3_nokern_3.png differ diff --git a/tests/fonts/diffs/paragraph4.png b/tests/fonts/diffs/paragraph4.png index 2d3c3e9..2835c3f 100644 Binary files a/tests/fonts/diffs/paragraph4.png and b/tests/fonts/diffs/paragraph4.png differ diff --git a/tests/fonts/diffs/paragraph4_2.png b/tests/fonts/diffs/paragraph4_2.png index 4799708..abd0af9 100644 Binary files a/tests/fonts/diffs/paragraph4_2.png and b/tests/fonts/diffs/paragraph4_2.png differ diff --git a/tests/fonts/diffs/paragraph4_3.png b/tests/fonts/diffs/paragraph4_3.png index 997f544..4e222ec 100644 Binary files a/tests/fonts/diffs/paragraph4_3.png and b/tests/fonts/diffs/paragraph4_3.png differ diff --git a/tests/fonts/diffs/paragraph4_nokern.png b/tests/fonts/diffs/paragraph4_nokern.png index 1c91a99..5761068 100644 Binary files a/tests/fonts/diffs/paragraph4_nokern.png and b/tests/fonts/diffs/paragraph4_nokern.png differ diff --git a/tests/fonts/diffs/paragraph4_nokern_3.png b/tests/fonts/diffs/paragraph4_nokern_3.png index a2c420a..e2b27a5 100644 Binary files a/tests/fonts/diffs/paragraph4_nokern_3.png and b/tests/fonts/diffs/paragraph4_nokern_3.png differ diff --git a/tests/fonts/diffs/paragraph5.png b/tests/fonts/diffs/paragraph5.png index 651c63c..198c715 100644 Binary files a/tests/fonts/diffs/paragraph5.png and b/tests/fonts/diffs/paragraph5.png differ diff --git a/tests/fonts/diffs/paragraph5_2.png b/tests/fonts/diffs/paragraph5_2.png index c95b103..cbc1d30 100644 Binary files a/tests/fonts/diffs/paragraph5_2.png and b/tests/fonts/diffs/paragraph5_2.png differ diff --git a/tests/fonts/diffs/paragraph5_3.png b/tests/fonts/diffs/paragraph5_3.png index 119d697..9700156 100644 Binary files a/tests/fonts/diffs/paragraph5_3.png and b/tests/fonts/diffs/paragraph5_3.png differ diff --git a/tests/fonts/diffs/paragraph5_nokern.png b/tests/fonts/diffs/paragraph5_nokern.png index 148a080..175abc2 100644 Binary files a/tests/fonts/diffs/paragraph5_nokern.png and b/tests/fonts/diffs/paragraph5_nokern.png differ diff --git a/tests/fonts/diffs/paragraph5_nokern_2.png b/tests/fonts/diffs/paragraph5_nokern_2.png index cddcab0..71ffc83 100644 Binary files a/tests/fonts/diffs/paragraph5_nokern_2.png and b/tests/fonts/diffs/paragraph5_nokern_2.png differ diff --git a/tests/fonts/diffs/paragraph5_nokern_3.png b/tests/fonts/diffs/paragraph5_nokern_3.png index 5c0580c..35ea556 100644 Binary files a/tests/fonts/diffs/paragraph5_nokern_3.png and b/tests/fonts/diffs/paragraph5_nokern_3.png differ diff --git a/tests/fonts/diffs/spans7.png b/tests/fonts/diffs/spans7.png new file mode 100644 index 0000000..53340f5 Binary files /dev/null and b/tests/fonts/diffs/spans7.png differ diff --git a/tests/fonts/diffs/strikethrough3.png b/tests/fonts/diffs/strikethrough3.png index ebbbcf1..fb8ebe3 100644 Binary files a/tests/fonts/diffs/strikethrough3.png and b/tests/fonts/diffs/strikethrough3.png differ diff --git a/tests/fonts/diffs/underline3.png b/tests/fonts/diffs/underline3.png index 9b4929d..aab00ef 100644 Binary files a/tests/fonts/diffs/underline3.png and b/tests/fonts/diffs/underline3.png differ diff --git a/tests/fonts/masters/spans7.png b/tests/fonts/masters/spans7.png new file mode 100644 index 0000000..873dfe6 Binary files /dev/null and b/tests/fonts/masters/spans7.png differ diff --git a/tests/fonts/rendered/customlineheight.png b/tests/fonts/rendered/customlineheight.png index 03bc16a..62aeba9 100644 Binary files a/tests/fonts/rendered/customlineheight.png and b/tests/fonts/rendered/customlineheight.png differ diff --git a/tests/fonts/rendered/paragraph1.png b/tests/fonts/rendered/paragraph1.png index b32dc7d..f54e123 100644 Binary files a/tests/fonts/rendered/paragraph1.png and b/tests/fonts/rendered/paragraph1.png differ diff --git a/tests/fonts/rendered/paragraph1_nokern.png b/tests/fonts/rendered/paragraph1_nokern.png index f6150f2..c5507f7 100644 Binary files a/tests/fonts/rendered/paragraph1_nokern.png and b/tests/fonts/rendered/paragraph1_nokern.png differ diff --git a/tests/fonts/rendered/paragraph3_nokern.png b/tests/fonts/rendered/paragraph3_nokern.png index 161f1fd..69df16f 100644 Binary files a/tests/fonts/rendered/paragraph3_nokern.png and b/tests/fonts/rendered/paragraph3_nokern.png differ diff --git a/tests/fonts/rendered/paragraph3_nokern_2.png b/tests/fonts/rendered/paragraph3_nokern_2.png index 16f6cdc..a4276be 100644 Binary files a/tests/fonts/rendered/paragraph3_nokern_2.png and b/tests/fonts/rendered/paragraph3_nokern_2.png differ diff --git a/tests/fonts/rendered/paragraph3_nokern_3.png b/tests/fonts/rendered/paragraph3_nokern_3.png index 3a17f4f..5e68386 100644 Binary files a/tests/fonts/rendered/paragraph3_nokern_3.png and b/tests/fonts/rendered/paragraph3_nokern_3.png differ diff --git a/tests/fonts/rendered/paragraph4.png b/tests/fonts/rendered/paragraph4.png index 04079eb..35322f2 100644 Binary files a/tests/fonts/rendered/paragraph4.png and b/tests/fonts/rendered/paragraph4.png differ diff --git a/tests/fonts/rendered/paragraph4_2.png b/tests/fonts/rendered/paragraph4_2.png index 954b2bd..de45d23 100644 Binary files a/tests/fonts/rendered/paragraph4_2.png and b/tests/fonts/rendered/paragraph4_2.png differ diff --git a/tests/fonts/rendered/paragraph4_3.png b/tests/fonts/rendered/paragraph4_3.png index 2e8f592..e4d3b1f 100644 Binary files a/tests/fonts/rendered/paragraph4_3.png and b/tests/fonts/rendered/paragraph4_3.png differ diff --git a/tests/fonts/rendered/paragraph4_nokern.png b/tests/fonts/rendered/paragraph4_nokern.png index 29bdbbe..948cb58 100644 Binary files a/tests/fonts/rendered/paragraph4_nokern.png and b/tests/fonts/rendered/paragraph4_nokern.png differ diff --git a/tests/fonts/rendered/paragraph4_nokern_3.png b/tests/fonts/rendered/paragraph4_nokern_3.png index 7e142d2..307f9e2 100644 Binary files a/tests/fonts/rendered/paragraph4_nokern_3.png and b/tests/fonts/rendered/paragraph4_nokern_3.png differ diff --git a/tests/fonts/rendered/paragraph5.png b/tests/fonts/rendered/paragraph5.png index 8ad257e..3a3f5a0 100644 Binary files a/tests/fonts/rendered/paragraph5.png and b/tests/fonts/rendered/paragraph5.png differ diff --git a/tests/fonts/rendered/paragraph5_2.png b/tests/fonts/rendered/paragraph5_2.png index 480f7d5..f94a250 100644 Binary files a/tests/fonts/rendered/paragraph5_2.png and b/tests/fonts/rendered/paragraph5_2.png differ diff --git a/tests/fonts/rendered/paragraph5_3.png b/tests/fonts/rendered/paragraph5_3.png index 055229a..8cc3ed5 100644 Binary files a/tests/fonts/rendered/paragraph5_3.png and b/tests/fonts/rendered/paragraph5_3.png differ diff --git a/tests/fonts/rendered/paragraph5_nokern.png b/tests/fonts/rendered/paragraph5_nokern.png index 2c85d5d..810ae2d 100644 Binary files a/tests/fonts/rendered/paragraph5_nokern.png and b/tests/fonts/rendered/paragraph5_nokern.png differ diff --git a/tests/fonts/rendered/paragraph5_nokern_2.png b/tests/fonts/rendered/paragraph5_nokern_2.png index 98f980b..81d4c42 100644 Binary files a/tests/fonts/rendered/paragraph5_nokern_2.png and b/tests/fonts/rendered/paragraph5_nokern_2.png differ diff --git a/tests/fonts/rendered/paragraph5_nokern_3.png b/tests/fonts/rendered/paragraph5_nokern_3.png index 583423b..05a776b 100644 Binary files a/tests/fonts/rendered/paragraph5_nokern_3.png and b/tests/fonts/rendered/paragraph5_nokern_3.png differ diff --git a/tests/fonts/rendered/spans7.png b/tests/fonts/rendered/spans7.png new file mode 100644 index 0000000..f65f238 Binary files /dev/null and b/tests/fonts/rendered/spans7.png differ diff --git a/tests/fonts/rendered/strikethrough3.png b/tests/fonts/rendered/strikethrough3.png index b045282..dccaa29 100644 Binary files a/tests/fonts/rendered/strikethrough3.png and b/tests/fonts/rendered/strikethrough3.png differ diff --git a/tests/fonts/rendered/underline3.png b/tests/fonts/rendered/underline3.png index a76ade5..efee3c1 100644 Binary files a/tests/fonts/rendered/underline3.png and b/tests/fonts/rendered/underline3.png differ diff --git a/tests/test_fonts.nim b/tests/test_fonts.nim index 71c9b9b..00f33f3 100644 --- a/tests/test_fonts.nim +++ b/tests/test_fonts.nim @@ -16,14 +16,14 @@ block: var font = readFont("tests/fonts/Roboto-Regular_1.ttf") font.size = 24 - let bounds = font.computeBounds("Word") + let bounds = font.layoutBounds("Word") doAssert bounds == vec2(56, 28) block: var font = readFont("tests/fonts/Roboto-Regular_1.ttf") font.size = 24 - let bounds = font.computeBounds("Word\n") + let bounds = font.layoutBounds("Word\n") doAssert bounds == vec2(56, 56) block: @@ -996,6 +996,43 @@ block: doDiff(image, "spans6") +block: + + let typeface1 = readTypeface("tests/fonts/PinyonScript.ttf") + + var font1 = newFont(typeface1) + font1.size = 82 + font1.lineHeight = 60 + font1.paint = "#000000" + + let spans = @[ + newSpan("Fancy text", font1), + ] + + let image = newImage(400, 400) + image.fill(rgba(255, 255, 255, 255)) + let ctx = newContext(image) + ctx.fillStyle = "#FFD6D6" + ctx.fillRect(rect(40, 170, 320, 60)) + + let + arrangement = typeset(spans, bounds = vec2(320, 60)) + snappedBounds = arrangement.computeBounds().snapToPixels() + textImage = newImage(snappedBounds.w.int, snappedBounds.h.int) + textImage.fillText(arrangement, translate(-snappedBounds.xy)) + + image.draw(textImage, translate(snappedBounds.xy + vec2(40, 170))) + + # Enable this to show bounds + # ctx.strokeStyle = "#FF0000" + # ctx.translate(vec2(40, 170)) + # ctx.strokeRect(arrangement.computeBounds()) + + # Enable this to show how text is drawing directly + # image.fillText(arrangement, translate(vec2(40, 170))) + + doDiff(image, "spans7") + block: var font = readFont("tests/fonts/Roboto-Regular_1.ttf") font.size = 36