Skip to content

Commit

Permalink
start on new htmlcanvas text structure
Browse files Browse the repository at this point in the history
  • Loading branch information
kkoreilly committed Feb 8, 2025
1 parent 28491d5 commit b4ab8f9
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 38 deletions.
38 changes: 0 additions & 38 deletions paint/renderers/htmlcanvas/htmlcanvas.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,7 @@
package htmlcanvas

import (
"fmt"
"image"
"strings"
"syscall/js"

"cogentcore.org/core/colors"
Expand All @@ -23,8 +21,6 @@ import (
"cogentcore.org/core/paint/render"
"cogentcore.org/core/styles"
"cogentcore.org/core/styles/units"
"cogentcore.org/core/text/rich"
"cogentcore.org/core/text/shaped"
)

// Renderer is an HTML canvas renderer.
Expand Down Expand Up @@ -222,40 +218,6 @@ func (rs *Renderer) RenderPath(pt *render.Path) {
}
}

func (rs *Renderer) RenderText(text *render.Text) {
// TODO: improve
for _, line := range text.Text.Lines {
for i, run := range line.Runs {
span := line.Source[i]
st := &rich.Style{}
raw := st.FromRunes(span)

rs.applyTextStyle(st, run, text)
// TODO: probably should do something better for pos
pos := run.MaxBounds.Max.Add(line.Offset).Add(text.Position)
rs.ctx.Call("fillText", string(raw), pos.X, pos.Y)
}
}
}

// applyTextStyle applies the given [rich.Style] to the HTML canvas context.
func (rs *Renderer) applyTextStyle(s *rich.Style, run shaped.Run, text *render.Text) {
// See https://developer.mozilla.org/en-US/docs/Web/CSS/font
// TODO: fix font weight, font size, line height, font family
parts := []string{s.Slant.String(), "normal", s.Weight.String(), s.Stretch.String(), fmt.Sprintf("%gpx/%g", s.Size*text.Text.FontSize, text.Text.LineHeight), s.Family.String()}
rs.ctx.Set("font", strings.Join(parts, " "))

// TODO: use caching like in RenderPath?
if run.FillColor == nil {
run.FillColor = colors.Uniform(text.Text.Color)
}
// if run.StrokeColor == nil {
// run.StrokeColor = ctx.Style.Stroke.Color
// }
rs.ctx.Set("fillStyle", rs.imageToStyle(run.FillColor))
rs.ctx.Set("strokeStyle", rs.imageToStyle(run.StrokeColor))
}

func jsAwait(v js.Value) (result js.Value, ok bool) { // TODO: use wgpu version
// COPIED FROM https://go-review.googlesource.com/c/go/+/150917/
if v.Type() != js.TypeObject || v.Get("then").Type() != js.TypeFunction {
Expand Down
106 changes: 106 additions & 0 deletions paint/renderers/htmlcanvas/text.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
// Copyright (c) 2025, Cogent Core. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

//go:build js

package htmlcanvas

import (
"fmt"
"image"
"strings"

"cogentcore.org/core/colors"
"cogentcore.org/core/math32"
"cogentcore.org/core/paint/render"
"cogentcore.org/core/text/rich"
"cogentcore.org/core/text/shaped"
)

// RenderText rasterizes the given Text
func (rs *Renderer) RenderText(txt *render.Text) {
rs.TextLines(txt.Text, &txt.Context, txt.Position)
}

// TextLines rasterizes the given shaped.Lines.
// The text will be drawn starting at the start pixel position, which specifies the
// left baseline location of the first text item..
func (rs *Renderer) TextLines(lns *shaped.Lines, ctx *render.Context, pos math32.Vector2) {
start := pos.Add(lns.Offset)
// rs.Scanner.SetClip(ctx.Bounds.Rect.ToRect())
clr := colors.Uniform(lns.Color)
runes := lns.Source.Join() // TODO: bad for performance with append
for li := range lns.Lines {
ln := &lns.Lines[li]
rs.TextLine(ln, lns, runes, clr, start) // todo: start + offset
}
}

// TextLine rasterizes the given shaped.Line.
func (rs *Renderer) TextLine(ln *shaped.Line, lns *shaped.Lines, runes []rune, clr image.Image, start math32.Vector2) {
off := start.Add(ln.Offset)
for ri := range ln.Runs {
run := &ln.Runs[ri]
rs.TextRun(run, ln, lns, runes, clr, off)
if run.Direction.IsVertical() {
off.Y += math32.FromFixed(run.Advance)
} else {
off.X += math32.FromFixed(run.Advance)
}
}
}

// TextRun rasterizes the given text run into the output image using the
// font face set in the shaping.
// The text will be drawn starting at the start pixel position.
func (rs *Renderer) TextRun(run *shaped.Run, ln *shaped.Line, lns *shaped.Lines, runes []rune, clr image.Image, start math32.Vector2) {
// todo: render strike-through
// dir := run.Direction
// rbb := run.MaxBounds.Translate(start)
if run.Background != nil {
// rs.FillBounds(rbb, run.Background) TODO
}
if len(ln.Selections) > 0 {
for _, sel := range ln.Selections {
rsel := sel.Intersect(run.Runes())
if rsel.Len() > 0 {
fi := run.FirstGlyphAt(rsel.Start)
li := run.LastGlyphAt(rsel.End - 1)
if fi >= 0 && li >= fi {
// sbb := run.GlyphRegionBounds(fi, li) TODO
// rs.FillBounds(sbb.Translate(start), lns.SelectionColor) TODO
}
}
}
}
// fill := clr
// if run.FillColor != nil {
// fill = run.FillColor
// }
// stroke := run.StrokeColor
// fsz := math32.FromFixed(run.Size)

region := run.Runes()
raw := runes[region.Start:region.End]
rs.ctx.Call("fillText", string(raw), start.X, start.Y)
}

// applyTextStyle applies the given [rich.Style] to the HTML canvas context.
func (rs *Renderer) applyTextStyle(s *rich.Style, run shaped.Run, text *render.Text) {
// See https://developer.mozilla.org/en-US/docs/Web/CSS/font
// TODO: fix font weight, font size, line height, font family
fmt.Println(s.Weight.String())
parts := []string{s.Slant.String(), "normal", s.Weight.String(), s.Stretch.String(), fmt.Sprintf("%gpx/%g", s.Size*text.Text.FontSize, text.Text.LineHeight), s.Family.String()}
rs.ctx.Set("font", strings.Join(parts, " "))

// TODO: use caching like in RenderPath?
if run.FillColor == nil {
run.FillColor = colors.Uniform(text.Text.Color)
}
// if run.StrokeColor == nil {
// run.StrokeColor = ctx.Style.Stroke.Color
// }
rs.ctx.Set("fillStyle", rs.imageToStyle(run.FillColor))
rs.ctx.Set("strokeStyle", rs.imageToStyle(run.StrokeColor))
}

0 comments on commit b4ab8f9

Please sign in to comment.