Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

jk/glyph indices #2139

Merged
merged 25 commits into from
Aug 24, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
cf3f228
switch from chars to Culong glyph indices
jkrumbiegel Jul 14, 2022
e63259a
save glyph indices in glyphcollection
jkrumbiegel Jul 14, 2022
7803e88
draw glyphs by index in cairomakie
jkrumbiegel Jul 14, 2022
a39f3bf
remove new path
jkrumbiegel Jul 14, 2022
c6ffc78
add stub for MathTeXEngine
jkrumbiegel Jul 14, 2022
c3f0246
switch atlas etc to UInt64 glyph indices
jkrumbiegel Jul 16, 2022
800228f
remove show
jkrumbiegel Jul 16, 2022
f2e619c
Update CairoMakie/src/primitives.jl
jkrumbiegel Jul 18, 2022
9b271ab
remove deleted function
jkrumbiegel Jul 18, 2022
3c995c7
bump compat
jkrumbiegel Jul 19, 2022
3ab03c1
switch to FreeTypeAbstraction.glyph_index
jkrumbiegel Jul 19, 2022
5ca39fb
remove show [skip-ci]
jkrumbiegel Jul 19, 2022
ac024bc
Merge branch 'master' into jk/glyph-indices
jkrumbiegel Jul 22, 2022
96ecd72
add mathtexengine branch to ci everywhere
jkrumbiegel Jul 22, 2022
f34ae29
Merge branch 'master' into jk/glyph-indices
jkrumbiegel Aug 16, 2022
acb227e
remove special mathtex branch from ci
jkrumbiegel Aug 21, 2022
b86d574
bump wglmakie freetypeabstraction
jkrumbiegel Aug 21, 2022
4b3aab3
fix test
jkrumbiegel Aug 21, 2022
2b444dc
use represented_char for word wrapping logic
jkrumbiegel Aug 22, 2022
9ec448e
Merge branch 'master' into jk/glyph-indices
jkrumbiegel Aug 22, 2022
660cc41
Merge branch 'master' into jk/glyph-indices
jkrumbiegel Aug 24, 2022
d210568
reintroduce '\n' render hack
jkrumbiegel Aug 24, 2022
371aa5d
remove redundant glyph_index()
jkrumbiegel Aug 24, 2022
15ff283
revert autoformatter changes
jkrumbiegel Aug 24, 2022
63648cd
flip wrong argument order
jkrumbiegel Aug 24, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 21 additions & 6 deletions CairoMakie/src/primitives.jl
Original file line number Diff line number Diff line change
Expand Up @@ -425,19 +425,15 @@ function draw_glyph_collection(scene, ctx, position, glyph_collection, rotation,
)

Cairo.save(ctx)
Cairo.move_to(ctx, glyphpos...)
set_font_matrix(ctx, mat)
Cairo.show_text(ctx, string(glyph))
# show_text makes an implicite move_to at the end, which starts a new one point path.
# `new_path` clears that path so it doesn't end up as an artifact in the next stroke call
Cairo.new_path(ctx)
show_glyph(ctx, glyph, glyphpos...)
Cairo.restore(ctx)

if strokewidth > 0 && strokecolor != RGBAf(0, 0, 0, 0)
Cairo.save(ctx)
Cairo.move_to(ctx, glyphpos...)
set_font_matrix(ctx, mat)
Cairo.text_path(ctx, string(glyph))
glyph_path(ctx, glyph, glyphpos...)
Cairo.set_source_rgba(ctx, rgbatuple(strokecolor)...)
Cairo.set_line_width(ctx, strokewidth)
Cairo.stroke(ctx)
Expand All @@ -453,6 +449,25 @@ function draw_glyph_collection(scene, ctx, position, glyph_collection, rotation,
return
end

struct CairoGlyph
index::Culong
x::Cdouble
y::Cdouble
end

function show_glyph(ctx, glyph, x, y)
cg = Ref(CairoGlyph(glyph, x, y))
ccall((:cairo_show_glyphs, Cairo.libcairo),
Nothing, (Ptr{Nothing}, Ptr{CairoGlyph}, Cint),
ctx.ptr, cg, 1)
Comment on lines +460 to +462
Copy link
Member

@asinghvi17 asinghvi17 Jul 15, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this case you might use cairo_show_text_glyphs, which allow text to be highlighted. We had glyph support earlier but AFAIK the text highlighting issue is why we switched away from it. It would also allow the whole GlyphCollection to be rendered at once.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At this point I don't have the utf 8 anymore and it's not guaranteed that there is any, as for Mathtexengine

end
function glyph_path(ctx, glyph::Culong, x, y)
cg = Ref(CairoGlyph(glyph, x, y))
ccall((:cairo_glyph_path, Cairo.libcairo),
Nothing, (Ptr{Nothing}, Ptr{CairoGlyph}, Cint),
ctx.ptr, cg, 1)
end

################################################################################
# Heatmap, Image #
################################################################################
Expand Down
2 changes: 1 addition & 1 deletion GLMakie/Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ ColorTypes = "0.9, 0.10, 0.11"
Colors = "0.11, 0.12"
FileIO = "1.6"
FixedPointNumbers = "0.7, 0.8"
FreeTypeAbstraction = "0.8, 0.9"
FreeTypeAbstraction = "0.10"
GLFW = "3"
GeometryBasics = "0.4.1"
Makie = "=0.17.13"
Expand Down
4 changes: 2 additions & 2 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ FileIO = "1.6"
FixedPointNumbers = "0.6, 0.7, 0.8"
Formatting = "0.4"
FreeType = "3.0, 4.0"
FreeTypeAbstraction = "0.8, 0.9"
FreeTypeAbstraction = "0.10"
GeometryBasics = "0.4.2"
GridLayoutBase = "0.9"
ImageIO = "0.2, 0.3, 0.4, 0.5, 0.6"
Expand All @@ -74,7 +74,7 @@ KernelDensity = "0.5, 0.6"
LaTeXStrings = "1.2"
MakieCore = "=0.4.0"
Match = "1.1"
MathTeXEngine = "0.4"
MathTeXEngine = "0.5"
Observables = "0.5.1"
OffsetArrays = "1"
Packing = "0.4"
Expand Down
2 changes: 1 addition & 1 deletion WGLMakie/Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"
[compat]
Colors = "0.11, 0.12"
FileIO = "1.1"
FreeTypeAbstraction = "0.8, 0.9"
FreeTypeAbstraction = "0.10"
GeometryBasics = "0.4.1"
Hyperscript = "0.0.3, 0.0.4"
ImageMagick = "1.1"
Expand Down
43 changes: 7 additions & 36 deletions src/basic_recipes/text.jl
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ function texelems_and_glyph_collection(str::LaTeXString, fontscale_px, halign, v
scales_2d = [Vec2f(x[3] * Vec2f(fs)) for x in els]

texchars = [x[1] for x in els]
chars = [texchar.char for texchar in texchars]
glyphindices = [FreeTypeAbstraction.glyph_index(texchar) for texchar in texchars]
fonts = [texchar.font for texchar in texchars]
extents = GlyphExtent.(texchars)

Expand All @@ -181,23 +181,24 @@ function texelems_and_glyph_collection(str::LaTeXString, fontscale_px, halign, v
last_newline_idx = 1
newline_offset = Point3f(basepositions[1][1], 0f0, 0)

for i in eachindex(chars)
for i in eachindex(texchars)
basepositions[i] -= newline_offset
if chars[i] == ' ' || i == length(chars)
if texchars[i].represented_char == ' ' || i == length(texchars)
right_pos = basepositions[i][1] + width(bboxes[i])
if last_space_idx != 0 && right_pos > word_wrap_width
section_offset = basepositions[last_space_idx + 1][1]
lineheight = maximum((height(bb) for bb in bboxes[last_newline_idx:last_space_idx]))
last_newline_idx = last_space_idx+1
newline_offset += Point3f(section_offset, lineheight, 0)

chars[last_space_idx] = '\n'
# TODO: newlines don't really need to represented at all?
# chars[last_space_idx] = '\n'
for j in last_space_idx+1:i
basepositions[j] -= Point3f(section_offset, lineheight, 0)
end
end
last_space_idx = i
elseif chars[i] == '\n'
elseif texchars[i].represented_char == '\n'
last_space_idx = 0
end
end
Expand Down Expand Up @@ -229,38 +230,8 @@ function texelems_and_glyph_collection(str::LaTeXString, fontscale_px, halign, v
positions = basepositions .- Ref(shift)
positions .= Ref(rot) .* positions

# # we replace VLine and HLine with characters that are specifically scaled and positioned
# # such that they match line length and thickness
# for (el, position, _) in all_els
# el isa MathTeXEngine.VLine || el isa MathTeXEngine.HLine || continue
# if el isa MathTeXEngine.HLine
# w, h = el.width, el.thickness
# else
# w, h = el.thickness, el.height
# end
# font = to_font("TeX Gyre Heros Makie")
# c = el isa MathTeXEngine.HLine ? '_' : '|'
# fext = get_extent(font, c)
# inkbb = FreeTypeAbstraction.inkboundingbox(fext)
# w_ink = width(inkbb)
# h_ink = height(inkbb)
# ori = inkbb.origin

# char_scale = Vec2f(w / w_ink, h / h_ink) * fs

# pos_scaled = fs * Vec2f(position)
# pos_inkshifted = pos_scaled - char_scale * ori - Vec2f(0, h_ink / 2) # TODO fix for VLine
# pos_final = rot * Vec3f((pos_inkshifted - Vec2f(shift[Vec(1, 2)]))..., 0)

# push!(positions, pos_final)
# push!(chars, c)
# push!(fonts, font)
# push!(extents, GlyphExtent(font, c))
# push!(scales_2d, char_scale)
# end

pre_align_gl = GlyphCollection(
chars,
glyphindices,
fonts,
Point3f.(positions),
extents,
Expand Down
2 changes: 1 addition & 1 deletion src/layouting/layouting.jl
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ function glyph_collection(
# interactive features that need to know where characters begin and end
per_char(attr) = collect(attribute_per_char(str, attr)) # attribute_per_char returns generators
return GlyphCollection(
[x.char for x in charinfos],
[FreeTypeAbstraction.glyph_index(x.font, x.char) for x in charinfos],
[x.font for x in charinfos],
reduce(vcat, charorigins),
[x.extent for x in charinfos],
Expand Down
2 changes: 1 addition & 1 deletion src/types.jl
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,7 @@ end
Stores information about the glyphs in a string that had a layout calculated for them.
"""
struct GlyphCollection
glyphs::Vector{Char}
glyphs::Vector{UInt64}
fonts::Vector{FTFont}
origins::Vector{Point3f}
extents::Vector{GlyphExtent}
Expand Down
59 changes: 23 additions & 36 deletions src/utilities/texture_atlas.jl
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
using FreeTypeAbstraction: iter_or_array

mutable struct TextureAtlas
rectangle_packer::RectanglePacker
mapping::Dict{Tuple{Char, String}, Int} # styled glyph to index in sprite_attributes
mapping::Dict{Tuple{UInt64, String}, Int} # styled glyph to index in sprite_attributes
index::Int
data::Matrix{Float16}
# rectangles we rendered our glyphs into in normalized uv coordinates
Expand Down Expand Up @@ -42,7 +40,7 @@ end
function TextureAtlas(initial_size = TEXTURE_RESOLUTION[])
return TextureAtlas(
RectanglePacker(Rect2(0, 0, initial_size...)),
Dict{Tuple{Char, String}, Int}(),
Dict{Tuple{UInt64, String}, Int}(),
1,
# We use float max here to avoid texture bleed. See #2096
fill(Float16(0.5PIXELSIZE_IN_ATLAS[] + GLYPH_PADDING[]), initial_size...),
Expand Down Expand Up @@ -150,54 +148,42 @@ end
Finds the best font for a character from a list of fallback fonts, that get chosen
if `font` can't represent char `c`
"""
function find_font_for_char(c::Char, font::NativeFont)
FT_Get_Char_Index(font, c) != 0 && return font
function find_font_for_char(glyph, font::NativeFont)
FreeTypeAbstraction.glyph_index(font, glyph) != 0 && return font
# it seems that linebreaks are not found which messes up font metrics
# if another font is selected just for those chars
c in ('\n', '\r', '\t') && return font
glyph in ('\n', '\r', '\t') && return font
for afont in alternativefonts()
if FT_Get_Char_Index(afont, c) != 0
if FreeTypeAbstraction.glyph_index(afont, glyph) != 0
return afont
end
end
error("Can't represent character $(c) with any fallback font nor $(font.family_name)!")
error("Can't represent character $(glyph) with any fallback font nor $(font.family_name)!")
end

function glyph_index!(atlas::TextureAtlas, c::Char, font::NativeFont)
if FT_Get_Char_Index(font, c) == 0
function glyph_index!(atlas::TextureAtlas, glyph, font::NativeFont)
if FreeTypeAbstraction.glyph_index(font, glyph) == 0
for afont in alternativefonts()
if FT_Get_Char_Index(afont, c) != 0
if FreeTypeAbstraction.glyph_index(afont, glyph) != 0
font = afont
end
end
end
return insert_glyph!(atlas, c, font)
return insert_glyph!(atlas, glyph, font)
end


function glyph_uv_width!(atlas::TextureAtlas, c::Char, font::NativeFont)
return atlas.uv_rectangles[glyph_index!(atlas, c, font)]
function glyph_uv_width!(atlas::TextureAtlas, glyph, font::NativeFont)
return atlas.uv_rectangles[glyph_index!(atlas, glyph, font)]
end

function glyph_uv_width!(c::Char)
return glyph_uv_width!(get_texture_atlas(), c, defaultfont())
function glyph_uv_width!(glyph)
return glyph_uv_width!(get_texture_atlas(), glyph, defaultfont())
end

# function glyph_boundingbox(c::Char, font::NativeFont, pixelsize)
# if FT_Get_Char_Index(font, c) == 0
# for afont in alternativefonts()
# if FT_Get_Char_Index(afont, c) != 0
# font = afont
# break
# end
# end
# end
# bb, ext = FreeTypeAbstraction.metrics_bb(c, font, pixelsize)
# return bb
# end

function insert_glyph!(atlas::TextureAtlas, glyph::Char, font::NativeFont)
return get!(atlas.mapping, (glyph, FreeTypeAbstraction.fontname(font))) do
function insert_glyph!(atlas::TextureAtlas, glyph, font::NativeFont)
glyphindex = FreeTypeAbstraction.glyph_index(font, glyph)
return get!(atlas.mapping, (glyphindex, FreeTypeAbstraction.fontname(font))) do
# We save glyphs as signed distance fields, i.e. we save the distance
# a pixel is away from the edge of a symbol (continuous at the edge).
# To get accurate distances we want to draw the symbol at high
Expand All @@ -210,7 +196,7 @@ function insert_glyph!(atlas::TextureAtlas, glyph::Char, font::NativeFont)
# resulting sdf.
pad = GLYPH_PADDING[]

uv_pixel = render(atlas, glyph, font, downsample, pad)
uv_pixel = render(atlas, glyphindex, font, downsample, pad)
tex_size = Vec2f(size(atlas.data) .- 1) # starts at 1

# 0 based
Expand Down Expand Up @@ -269,11 +255,12 @@ function remove_font_render_callback!(f)
end
end

function render(atlas::TextureAtlas, glyph::Char, font, downsample=5, pad=6)
#select_font_face(cc, font)
if glyph == '\n' # don't render newline
function render(atlas::TextureAtlas, glyph, font, downsample=5, pad=6)
# TODO: Is this needed or should newline be filtered before this?
if FreeTypeAbstraction.glyph_index(font, glyph) == FreeTypeAbstraction.glyph_index(font, '\n') # don't render newline
glyph = ' '
end

# the target pixel size of our distance field
pixelsize = PIXELSIZE_IN_ATLAS[]
# we render the font `downsample` sizes times bigger
Expand Down
2 changes: 1 addition & 1 deletion test/text.jl
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
])

@test glyph_collection isa Makie.GlyphCollection
@test glyph_collection.glyphs == chars
@test glyph_collection.glyphs == FreeTypeAbstraction.glyph_index.(font, chars)
@test glyph_collection.fonts == [font for _ in 1:4]
@test all(isapprox.(glyph_collection.origins, [Point3f(x, 0, 0) for x in origins], atol = 1e-10))
@test glyph_collection.scales.sv == [Vec2f(p.textsize[]) for _ in 1:4]
Expand Down