mirror of
https://github.com/bloeys/nterm.git
synced 2025-12-29 06:28:20 +00:00
Alhamdullah basic arabic rendering :D
This commit is contained in:
247
glyphs/glyphs.go
247
glyphs/glyphs.go
@ -61,83 +61,167 @@ func (gr *GlyphRend) DrawTextOpenGL01(text string, screenPos *gglm.Vec3, color *
|
|||||||
//Color is RGBA in the range [0,1].
|
//Color is RGBA in the range [0,1].
|
||||||
func (gr *GlyphRend) DrawTextOpenGLAbs(text string, screenPos *gglm.Vec3, color *gglm.Vec4) {
|
func (gr *GlyphRend) DrawTextOpenGLAbs(text string, screenPos *gglm.Vec3, color *gglm.Vec4) {
|
||||||
|
|
||||||
//Prepass to pre-allocate the buffer
|
runs := gr.GetTextRuns(text)
|
||||||
rs := []rune(text)
|
if runs == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// startPos := screenPos.Clone()
|
|
||||||
pos := screenPos.Clone()
|
pos := screenPos.Clone()
|
||||||
advanceF32 := float32(gr.Atlas.Advance)
|
advanceF32 := float32(gr.Atlas.Advance)
|
||||||
lineHeightF32 := float32(gr.Atlas.LineHeight)
|
lineHeightF32 := float32(gr.Atlas.LineHeight)
|
||||||
scale := gglm.NewVec2(advanceF32, lineHeightF32)
|
scale := gglm.NewVec2(advanceF32, lineHeightF32)
|
||||||
|
|
||||||
prevRune := invalidRune
|
|
||||||
buffIndex := gr.GlyphCount * floatsPerGlyph
|
buffIndex := gr.GlyphCount * floatsPerGlyph
|
||||||
for i := 0; i < len(rs); i++ {
|
|
||||||
|
|
||||||
r := rs[i]
|
for _, run := range runs {
|
||||||
if r == '\n' {
|
|
||||||
screenPos.SetY(screenPos.Y() - lineHeightF32)
|
rs := run
|
||||||
pos = screenPos.Clone()
|
prevRune := invalidRune
|
||||||
prevRune = r
|
bidiCat := RuneInfos[rs[0]].BidiCat
|
||||||
continue
|
isLtr := !(bidiCat == BidiCategory_R || bidiCat == BidiCategory_AL || bidiCat == BidiCategory_RLE || bidiCat == BidiCategory_RLO || bidiCat == BidiCategory_RLI || bidiCat == BidiCategory_RLM)
|
||||||
} else if r == ' ' {
|
|
||||||
pos.AddX(advanceF32)
|
if isLtr {
|
||||||
prevRune = r
|
|
||||||
continue
|
for i := 0; i < len(rs); i++ {
|
||||||
} else if r == '\t' {
|
|
||||||
pos.AddX(advanceF32 * float32(gr.SpacesPerTab))
|
r := rs[i]
|
||||||
prevRune = r
|
if r == '\n' {
|
||||||
continue
|
screenPos.SetY(screenPos.Y() - lineHeightF32)
|
||||||
}
|
pos = screenPos.Clone()
|
||||||
|
prevRune = r
|
||||||
|
continue
|
||||||
|
} else if r == ' ' {
|
||||||
|
pos.AddX(advanceF32)
|
||||||
|
prevRune = r
|
||||||
|
continue
|
||||||
|
} else if r == '\t' {
|
||||||
|
pos.AddX(advanceF32 * float32(gr.SpacesPerTab))
|
||||||
|
prevRune = r
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var g *FontAtlasGlyph
|
||||||
|
if i < len(rs)-1 {
|
||||||
|
//start or middle of sentence
|
||||||
|
g = gr.glyphFromRunes(r, prevRune, rs[i+1])
|
||||||
|
} else {
|
||||||
|
//Last character
|
||||||
|
g = gr.glyphFromRunes(r, prevRune, invalidRune)
|
||||||
|
}
|
||||||
|
|
||||||
|
//See: https://developer.apple.com/library/archive/documentation/TextFonts/Conceptual/CocoaTextArchitecture/Art/glyph_metrics_2x.png
|
||||||
|
//The uvs coming in make it so that glyphs are sitting on top of the baseline (no descent) and with horizontal bearing applied.
|
||||||
|
//So to position correctly we move them down by the descent amount.
|
||||||
|
drawPos := *pos
|
||||||
|
drawPos.SetX(drawPos.X())
|
||||||
|
drawPos.SetY(drawPos.Y() - g.Descent)
|
||||||
|
|
||||||
|
//Add the glyph information to the vbo
|
||||||
|
//UV
|
||||||
|
gr.GlyphVBO[buffIndex+0] = g.U
|
||||||
|
gr.GlyphVBO[buffIndex+1] = g.V
|
||||||
|
|
||||||
|
//Color
|
||||||
|
gr.GlyphVBO[buffIndex+2] = color.R()
|
||||||
|
gr.GlyphVBO[buffIndex+3] = color.G()
|
||||||
|
gr.GlyphVBO[buffIndex+4] = color.B()
|
||||||
|
gr.GlyphVBO[buffIndex+5] = color.A()
|
||||||
|
|
||||||
|
//Model Pos
|
||||||
|
gr.GlyphVBO[buffIndex+6] = roundF32(drawPos.X())
|
||||||
|
gr.GlyphVBO[buffIndex+7] = roundF32(drawPos.Y())
|
||||||
|
gr.GlyphVBO[buffIndex+8] = drawPos.Z()
|
||||||
|
|
||||||
|
//Model Scale
|
||||||
|
gr.GlyphVBO[buffIndex+9] = scale.X()
|
||||||
|
gr.GlyphVBO[buffIndex+10] = scale.Y()
|
||||||
|
|
||||||
|
gr.GlyphCount++
|
||||||
|
pos.AddX(advanceF32)
|
||||||
|
|
||||||
|
//If we fill the buffer we issue a draw call
|
||||||
|
if gr.GlyphCount == MaxGlyphsPerBatch {
|
||||||
|
gr.Draw()
|
||||||
|
buffIndex = 0
|
||||||
|
} else {
|
||||||
|
buffIndex += floatsPerGlyph
|
||||||
|
}
|
||||||
|
|
||||||
|
prevRune = r
|
||||||
|
}
|
||||||
|
|
||||||
var g *FontAtlasGlyph
|
|
||||||
if i < len(rs)-1 {
|
|
||||||
//start or middle of sentence
|
|
||||||
g = gr.glyphFromRunes(r, prevRune, rs[i+1])
|
|
||||||
} else {
|
} else {
|
||||||
//Last character
|
|
||||||
g = gr.glyphFromRunes(r, prevRune, invalidRune)
|
for i := len(rs) - 1; i >= 0; i-- {
|
||||||
|
|
||||||
|
r := rs[i]
|
||||||
|
if r == '\n' {
|
||||||
|
screenPos.SetY(screenPos.Y() - lineHeightF32)
|
||||||
|
pos = screenPos.Clone()
|
||||||
|
prevRune = r
|
||||||
|
continue
|
||||||
|
} else if r == ' ' {
|
||||||
|
pos.AddX(advanceF32)
|
||||||
|
prevRune = r
|
||||||
|
continue
|
||||||
|
} else if r == '\t' {
|
||||||
|
pos.AddX(advanceF32 * float32(gr.SpacesPerTab))
|
||||||
|
prevRune = r
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var g *FontAtlasGlyph
|
||||||
|
if i > 0 {
|
||||||
|
//start or middle of sentence
|
||||||
|
g = gr.glyphFromRunes(r, rs[i-1], prevRune)
|
||||||
|
} else {
|
||||||
|
//Last character
|
||||||
|
g = gr.glyphFromRunes(r, invalidRune, prevRune)
|
||||||
|
}
|
||||||
|
|
||||||
|
//See: https://developer.apple.com/library/archive/documentation/TextFonts/Conceptual/CocoaTextArchitecture/Art/glyph_metrics_2x.png
|
||||||
|
//The uvs coming in make it so that glyphs are sitting on top of the baseline (no descent) and with horizontal bearing applied.
|
||||||
|
//So to position correctly we move them down by the descent amount.
|
||||||
|
drawPos := *pos
|
||||||
|
drawPos.SetX(drawPos.X())
|
||||||
|
drawPos.SetY(drawPos.Y() - g.Descent)
|
||||||
|
|
||||||
|
//Add the glyph information to the vbo
|
||||||
|
//UV
|
||||||
|
gr.GlyphVBO[buffIndex+0] = g.U
|
||||||
|
gr.GlyphVBO[buffIndex+1] = g.V
|
||||||
|
|
||||||
|
//Color
|
||||||
|
gr.GlyphVBO[buffIndex+2] = color.R()
|
||||||
|
gr.GlyphVBO[buffIndex+3] = color.G()
|
||||||
|
gr.GlyphVBO[buffIndex+4] = color.B()
|
||||||
|
gr.GlyphVBO[buffIndex+5] = color.A()
|
||||||
|
|
||||||
|
//Model Pos
|
||||||
|
gr.GlyphVBO[buffIndex+6] = roundF32(drawPos.X())
|
||||||
|
gr.GlyphVBO[buffIndex+7] = roundF32(drawPos.Y())
|
||||||
|
gr.GlyphVBO[buffIndex+8] = drawPos.Z()
|
||||||
|
|
||||||
|
//Model Scale
|
||||||
|
gr.GlyphVBO[buffIndex+9] = scale.X()
|
||||||
|
gr.GlyphVBO[buffIndex+10] = scale.Y()
|
||||||
|
|
||||||
|
gr.GlyphCount++
|
||||||
|
pos.AddX(advanceF32)
|
||||||
|
|
||||||
|
//If we fill the buffer we issue a draw call
|
||||||
|
if gr.GlyphCount == MaxGlyphsPerBatch {
|
||||||
|
gr.Draw()
|
||||||
|
buffIndex = 0
|
||||||
|
} else {
|
||||||
|
buffIndex += floatsPerGlyph
|
||||||
|
}
|
||||||
|
|
||||||
|
prevRune = r
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//See: https://developer.apple.com/library/archive/documentation/TextFonts/Conceptual/CocoaTextArchitecture/Art/glyph_metrics_2x.png
|
|
||||||
//The uvs coming in make it so that glyphs are sitting on top of the baseline (no descent) and with horizontal bearing applied.
|
|
||||||
//So to position correctly we move them down by the descent amount.
|
|
||||||
drawPos := *pos
|
|
||||||
drawPos.SetX(drawPos.X())
|
|
||||||
drawPos.SetY(drawPos.Y() - g.Descent)
|
|
||||||
|
|
||||||
//Add the glyph information to the vbo
|
|
||||||
//UV
|
|
||||||
gr.GlyphVBO[buffIndex+0] = g.U
|
|
||||||
gr.GlyphVBO[buffIndex+1] = g.V
|
|
||||||
|
|
||||||
//Color
|
|
||||||
gr.GlyphVBO[buffIndex+2] = color.R()
|
|
||||||
gr.GlyphVBO[buffIndex+3] = color.G()
|
|
||||||
gr.GlyphVBO[buffIndex+4] = color.B()
|
|
||||||
gr.GlyphVBO[buffIndex+5] = color.A()
|
|
||||||
|
|
||||||
//Model Pos
|
|
||||||
gr.GlyphVBO[buffIndex+6] = roundF32(drawPos.X())
|
|
||||||
gr.GlyphVBO[buffIndex+7] = roundF32(drawPos.Y())
|
|
||||||
gr.GlyphVBO[buffIndex+8] = drawPos.Z()
|
|
||||||
|
|
||||||
//Model Scale
|
|
||||||
gr.GlyphVBO[buffIndex+9] = scale.X()
|
|
||||||
gr.GlyphVBO[buffIndex+10] = scale.Y()
|
|
||||||
|
|
||||||
gr.GlyphCount++
|
|
||||||
pos.AddX(advanceF32)
|
|
||||||
|
|
||||||
//If we fill the buffer we issue a draw call
|
|
||||||
if gr.GlyphCount == MaxGlyphsPerBatch {
|
|
||||||
gr.Draw()
|
|
||||||
buffIndex = 0
|
|
||||||
} else {
|
|
||||||
buffIndex += floatsPerGlyph
|
|
||||||
}
|
|
||||||
|
|
||||||
prevRune = r
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -146,7 +230,7 @@ func (gr *GlyphRend) GetTextRuns(t string) [][]rune {
|
|||||||
rs := []rune(t)
|
rs := []rune(t)
|
||||||
|
|
||||||
if len(rs) == 0 {
|
if len(rs) == 0 {
|
||||||
return [][]rune{}
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
runs := make([][]rune, 0, 1)
|
runs := make([][]rune, 0, 1)
|
||||||
@ -157,7 +241,7 @@ func (gr *GlyphRend) GetTextRuns(t string) [][]rune {
|
|||||||
|
|
||||||
r := rs[i]
|
r := rs[i]
|
||||||
ri := RuneInfos[r]
|
ri := RuneInfos[r]
|
||||||
if ri.ScriptTable == currRunScript || ri.ScriptTable == unicode.Common {
|
if ri.ScriptTable == currRunScript {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -167,9 +251,7 @@ func (gr *GlyphRend) GetTextRuns(t string) [][]rune {
|
|||||||
currRunScript = ri.ScriptTable
|
currRunScript = ri.ScriptTable
|
||||||
}
|
}
|
||||||
|
|
||||||
if runStartIndex != len(rs)-1 {
|
runs = append(runs, rs[runStartIndex:])
|
||||||
runs = append(runs, rs[runStartIndex:])
|
|
||||||
}
|
|
||||||
|
|
||||||
return runs
|
return runs
|
||||||
}
|
}
|
||||||
@ -202,6 +284,7 @@ func (gr *GlyphRend) glyphFromRunes(curr, prev, next rune) *FontAtlasGlyph {
|
|||||||
ctx = PosCtx_end
|
ctx = PosCtx_end
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//This is only needed for Arabic (I think)
|
||||||
switch ctx {
|
switch ctx {
|
||||||
case PosCtx_start:
|
case PosCtx_start:
|
||||||
|
|
||||||
@ -210,7 +293,7 @@ func (gr *GlyphRend) glyphFromRunes(curr, prev, next rune) *FontAtlasGlyph {
|
|||||||
|
|
||||||
otherRune := equivRunes[i]
|
otherRune := equivRunes[i]
|
||||||
otherRuneInfo := RuneInfos[otherRune]
|
otherRuneInfo := RuneInfos[otherRune]
|
||||||
if otherRuneInfo.DecompTag == DecompTags_initial {
|
if otherRuneInfo.DecompTag == DecompTag_initial {
|
||||||
curr = otherRune
|
curr = otherRune
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -218,7 +301,29 @@ func (gr *GlyphRend) glyphFromRunes(curr, prev, next rune) *FontAtlasGlyph {
|
|||||||
|
|
||||||
case PosCtx_mid:
|
case PosCtx_mid:
|
||||||
|
|
||||||
|
equivRunes := RuneInfos[curr].EquivalentRunes
|
||||||
|
for i := 0; i < len(equivRunes); i++ {
|
||||||
|
|
||||||
|
otherRune := equivRunes[i]
|
||||||
|
otherRuneInfo := RuneInfos[otherRune]
|
||||||
|
if otherRuneInfo.DecompTag == DecompTag_medial {
|
||||||
|
curr = otherRune
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
case PosCtx_end:
|
case PosCtx_end:
|
||||||
|
|
||||||
|
equivRunes := RuneInfos[curr].EquivalentRunes
|
||||||
|
for i := 0; i < len(equivRunes); i++ {
|
||||||
|
|
||||||
|
otherRune := equivRunes[i]
|
||||||
|
otherRuneInfo := RuneInfos[otherRune]
|
||||||
|
if otherRuneInfo.DecompTag == DecompTag_final {
|
||||||
|
curr = otherRune
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
g := gr.Atlas.Glyphs[curr]
|
g := gr.Atlas.Glyphs[curr]
|
||||||
|
|||||||
@ -84,7 +84,7 @@ type DecompTag uint8
|
|||||||
const (
|
const (
|
||||||
DecompTag_font DecompTag = iota // A font variant (e.g. a blackletter form).
|
DecompTag_font DecompTag = iota // A font variant (e.g. a blackletter form).
|
||||||
DecompTag_noBreak // A no-break version of a space or hyphen.
|
DecompTag_noBreak // A no-break version of a space or hyphen.
|
||||||
DecompTags_initial // An initial presentation form (Arabic).
|
DecompTag_initial // An initial presentation form (Arabic).
|
||||||
DecompTag_medial // A medial presentation form (Arabic).
|
DecompTag_medial // A medial presentation form (Arabic).
|
||||||
DecompTag_final // A final presentation form (Arabic).
|
DecompTag_final // A final presentation form (Arabic).
|
||||||
DecompTag_isolated // An isolated presentation form (Arabic).
|
DecompTag_isolated // An isolated presentation form (Arabic).
|
||||||
@ -108,7 +108,7 @@ func (cd DecompTag) String() string {
|
|||||||
return "font"
|
return "font"
|
||||||
case DecompTag_noBreak:
|
case DecompTag_noBreak:
|
||||||
return "noBreak"
|
return "noBreak"
|
||||||
case DecompTags_initial:
|
case DecompTag_initial:
|
||||||
return "initial"
|
return "initial"
|
||||||
case DecompTag_medial:
|
case DecompTag_medial:
|
||||||
return "medial"
|
return "medial"
|
||||||
@ -809,7 +809,7 @@ func charDecompMapStringToCharDecompMap(c string) DecompTag {
|
|||||||
case "<noBreak>":
|
case "<noBreak>":
|
||||||
return DecompTag_noBreak
|
return DecompTag_noBreak
|
||||||
case "<initial>":
|
case "<initial>":
|
||||||
return DecompTags_initial
|
return DecompTag_initial
|
||||||
case "<medial>":
|
case "<medial>":
|
||||||
return DecompTag_medial
|
return DecompTag_medial
|
||||||
case "<final>":
|
case "<final>":
|
||||||
|
|||||||
10
main.go
10
main.go
@ -101,8 +101,11 @@ func (p *program) Init() {
|
|||||||
p.gridMat = materials.NewMaterial("grid", "./res/shaders/grid.glsl")
|
p.gridMat = materials.NewMaterial("grid", "./res/shaders/grid.glsl")
|
||||||
p.handleWindowResize()
|
p.handleWindowResize()
|
||||||
|
|
||||||
// runs := p.GlyphRend.GetTextRuns("hello there يا friend")
|
runs := p.GlyphRend.GetTextRuns("hello مرحبا,")
|
||||||
// fmt.Printf("%+v\n%s\n%s\n%s\n", runs, string(runs[0]), string(runs[1]), string(runs[2]))
|
fmt.Printf("%+v\n", runs)
|
||||||
|
for _, r := range runs {
|
||||||
|
fmt.Printf("%s;\n", string(r))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *program) Update() {
|
func (p *program) Update() {
|
||||||
@ -190,7 +193,8 @@ func (p *program) Render() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
textColor := gglm.NewVec4(r, g, b, 1)
|
textColor := gglm.NewVec4(r, g, b, 1)
|
||||||
str := " مرحبا بك"
|
// str := " مرحبا بك"
|
||||||
|
str := "hello there يا friend. أسمي wow"
|
||||||
// str := " ijojo\n\n Hello there, friend|. pq?\n ABCDEFG\tHIJKLMNOPQRSTUVWXYZ\nمرحبا بك"
|
// str := " ijojo\n\n Hello there, friend|. pq?\n ABCDEFG\tHIJKLMNOPQRSTUVWXYZ\nمرحبا بك"
|
||||||
// str := " ijojo\n\n Hello there, friend|. pq?\n ABCDEFG\tHIJKLMNOPQRSTUVWXYZ"
|
// str := " ijojo\n\n Hello there, friend|. pq?\n ABCDEFG\tHIJKLMNOPQRSTUVWXYZ"
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user