diff --git a/lib/exterm/font.ex b/lib/exterm/font.ex
index bd72ce9..5ac2e4e 100644
--- a/lib/exterm/font.ex
+++ b/lib/exterm/font.ex
@@ -30,7 +30,7 @@ defmodule Exterm.Font do
if :gl.isList(id) do
:ok
else
- :ok = :gl.newList(id, :gl_const.gl_compile)
+ :ok = :gl.newList(id, :gl_const.gl_compile_and_execute)
:ok = render_glyph(font, codepoint)
:ok = :gl.endList()
:ok
@@ -55,10 +55,13 @@ defmodule Exterm.Font do
ttf = ttf(font.family)
if glyph = TTF.glyph(ttf, codepoint) do
Enum.each(glyph.contours, fn contour ->
- :gl.begin(:gl_const.gl_line_strip)
- Enum.each(contour, fn {x1, y1, _, _, _, _, _, _} ->
- :gl.vertex2f(x1, y1)
+ :gl.begin(:gl_const.gl_line_loop)
+ :gl.color3f(0.0, 0.0, 0.0)
+ Enum.each(TTF.Glyph.contour_segments(contour), fn control_point ->
+ IO.inspect(control_point)
+ :gl.vertex2f(control_point.a.x * 0.01, control_point.a.y * 0.01)
end)
+ :gl.end()
end)
end
end
diff --git a/lib/ttf.ex b/lib/ttf.ex
index fb80d38..bd43db5 100644
--- a/lib/ttf.ex
+++ b/lib/ttf.ex
@@ -3,6 +3,8 @@ defmodule TTF do
defstruct bounding_box: nil, cmap: [], cmap_subtable_count: nil, data: nil, glyph_count: nil, glyph_locations: nil, header: nil, loca_offset_format: nil, magic: nil, path: nil, table_count: nil, units_per_em: nil
+ alias TTF.Glyph
+
@cmap_platform_id_unicode 0
@cmap_unicode_2_bmp_only 3
@cmap_platform_id_apple 1
@@ -75,7 +77,8 @@ defmodule TTF do
def parse_cmap(ttf) do
cmap = table(ttf, "cmap")
- <<version::unsigned-big-16, subtable_count::unsigned-big-16, rest::binary>> = cmap
+ <<_version::unsigned-big-16, subtable_count::unsigned-big-16, rest::binary>> = cmap
+
%__MODULE__{ttf | cmap_subtable_count: subtable_count, cmap: []}
|> parse_cmap_subtable(cmap, rest, 0)
end
@@ -87,15 +90,15 @@ defmodule TTF do
ttf
|> parse_cmap_subtable(cmap, rest, i, platform_id, platform_specific_id, offset)
end
- def parse_cmap_subtable(ttf, cmap, binary, i, @cmap_platform_id_unicode, @cmap_unicode_2_bmp_only, offset) do
+ def parse_cmap_subtable(ttf, cmap, binary, i, @cmap_platform_id_unicode, @cmap_unicode_2_bmp_only, _offset) do
ttf
|> parse_cmap_subtable(cmap, binary, i + 1)
end
- def parse_cmap_subtable(ttf, cmap, binary, i, @cmap_platform_id_apple, _, offset) do
+ def parse_cmap_subtable(ttf, cmap, binary, i, @cmap_platform_id_apple, _, _offset) do
ttf
|> parse_cmap_subtable(cmap, binary, i + 1)
end
- def parse_cmap_subtable(ttf, cmap, binary, i, @cmap_platform_id_microsoft, @cmap_microsoft_unicode_bmp, offset) do
+ def parse_cmap_subtable(ttf, cmap, _binary, i, @cmap_platform_id_microsoft, @cmap_microsoft_unicode_bmp, offset) do
<<_::binary-size(offset), format::unsigned-big-16, rest::binary>> = cmap
if format != 4 do
raise ArgumentError, "invalid cmap subtable format #{format}"
@@ -103,32 +106,32 @@ defmodule TTF do
table_start_rest_byte_size = byte_size(rest) + 2
<<subtable_length::unsigned-big-16, _language_code::unsigned-big-16, segment_count::unsigned-big-16, _search_range::unsigned-big-16, _entry_selector::unsigned-big-16, _range_shift::unsigned-big-16, rest::binary>> = rest
segment_count = trunc(segment_count / 2)
- {end_codes, rest} = parse_cmap_subtable_array(rest, segment_count)
+ {end_codes, rest} = parse_ub16_array(rest, segment_count)
<<_::unsigned-16, rest::binary>> = rest
- {start_codes, rest} = parse_cmap_subtable_array(rest, segment_count)
- {id_deltas, rest} = parse_cmap_subtable_array(rest, segment_count)
- {id_range_offsets, rest} = parse_cmap_subtable_array(rest, segment_count)
- {start_codes, rest} = parse_cmap_subtable_array(rest, segment_count)
+ {start_codes, rest} = parse_ub16_array(rest, segment_count)
+ {id_deltas, rest} = parse_ub16_array(rest, segment_count)
+ id_deltas = id_deltas
+ |> Tuple.to_list()
+ |> Enum.map(&ub16_to_signed/1)
+ |> List.to_tuple()
+ {id_range_offsets, rest} = parse_ub16_array(rest, segment_count)
glyph_index_array_size = trunc((subtable_length - (table_start_rest_byte_size - byte_size(rest))) / 2)
- id_deltas = id_deltas |> Tuple.to_list() |> Enum.map(&ub16_to_signed/1) |> List.to_tuple()
- glyph_indexes = parse_cmap_subtable_array(rest, glyph_index_array_size)
+ glyph_indexes = parse_ub16_array(rest, glyph_index_array_size)
subtable = %{platform_id: @cmap_platform_id_microsoft, platform_specific_id: @cmap_microsoft_unicode_bmp, segment_count: segment_count, end_codes: end_codes, start_codes: start_codes, id_deltas: id_deltas, id_range_offsets: id_range_offsets, glyph_indexes: glyph_indexes}
%__MODULE__{ttf | cmap: [subtable | ttf.cmap]}
|> parse_cmap_subtable(cmap, rest, i + 1)
end
- def parse_cmap_subtable_array(subtable, size) do
- parse_cmap_subtable_array(subtable, size, [])
- end
- def parse_cmap_subtable_array(rest, 0, acc) do
+ def parse_ub16_array(binary, size), do: parse_ub16_array(binary, size, 0, [])
+ def parse_ub16_array(rest, size, i, acc) when i >= size do
{acc |> Enum.reverse() |> List.to_tuple(), rest}
end
- def parse_cmap_subtable_array(<<ub16::unsigned-big-16, rest::binary>>, size, acc) do
- parse_cmap_subtable_array(rest, size - 1, [ub16 | acc])
+ def parse_ub16_array(<<first::unsigned-big-16, rest::binary>>, size, i, acc) do
+ parse_ub16_array(rest, size, i + 1, [first | acc])
end
def ub16_to_signed(ub16) do
- if (band(0b1000000000000000, ub16) != 0) do
+ if (band(bsl(1, 15), ub16) != 0) do
- band(0xFFFF, bnot(ub16)) - 1
else
ub16
@@ -173,14 +176,10 @@ defmodule TTF do
ttf
end
- def parse_glyph(ttf = %{glyphs: glyphs}, index) do
- ttf
- end
-
def codepoint_to_font_index(font, codepoint) do
codepoint_to_font_index(font, codepoint, 0, hd(font.cmap).segment_count)
end
- def codepoint_to_font_index(font, codepoint, i, segment_count) when i >= segment_count do
+ def codepoint_to_font_index(_font, _codepoint, i, segment_count) when i >= segment_count do
nil
end
def codepoint_to_font_index(font, codepoint, i, _segment_count) do
@@ -200,6 +199,11 @@ defmodule TTF do
nil
end
end
+
+ def glyph_location(ttf, index) do
+ elem(ttf.glyph_locations, index)
+ end
+
def has_glyph?(ttf, codepoint) do
if codepoint_to_font_index(ttf, codepoint) do
true
@@ -211,7 +215,14 @@ defmodule TTF do
def glyph(ttf, codepoint) do
id = codepoint_to_font_index(ttf, codepoint) || 0
glyf = table(ttf, "glyf")
-
- nil
+ location = glyph_location(ttf, id)
+ <<_::binary-size(location), contour_count::signed-big-16, xmin::unsigned-big-16, ymin::unsigned-big-16, xmax::unsigned-big-16, ymax::unsigned-big-16, rest::binary>> = glyf
+ IO.inspect([contour_count: contour_count, id: id, location: location])
+ glyph = %Glyph{bounding_box: {xmin, ymin, xmax, ymax}, codepoint: codepoint, contour_count: contour_count, ttf: ttf}
+ if (contour_count == -1) do
+ glyph |> Glyph.CompoundContours.parse(rest)
+ else
+ glyph |> Glyph.SimpleContours.parse(rest)
+ end
end
end
diff --git a/lib/ttf/control_point.ex b/lib/ttf/control_point.ex
new file mode 100644
index 0000000..d7b8e50
--- /dev/null
+++ b/lib/ttf/control_point.ex
@@ -0,0 +1,5 @@
+defmodule TTF.ControlPoint do
+
+ defstruct x: nil, y: nil, on_curve?: nil
+
+end
diff --git a/lib/ttf/glyph.ex b/lib/ttf/glyph.ex
new file mode 100644
index 0000000..99967ba
--- /dev/null
+++ b/lib/ttf/glyph.ex
@@ -0,0 +1,71 @@
+defmodule TTF.Glyph do
+
+ defstruct bounding_box: nil, codepoint: nil, contour_count: nil, contour_endpoint_indexes: nil, contours: nil, control_points: nil, flags: nil, n_points: nil, name: nil, ttf: nil
+
+ def contour_segments([]) do
+ []
+ end
+ def contour_segments(contour) do
+ length = Enum.count(contour)
+ i = 1
+ stack = nil
+ next = nil
+ start = Enum.at(contour, 0)
+ mid = nil
+ end_ = nil
+ segments = []
+ contour_segments(contour, length, i, stack, next, start, mid, end_, segments)
+ end
+
+ def contour_segments(contour, length, i, stack, next, start, mid, end_, segments) do
+ mid = nil
+ {next, i} = contour_next_point(contour, length, i)
+ if next == nil do
+ mid = stack
+ end_ = Enum.at(contour, 0)
+ contour_segments_body(contour, length, i, stack, next, start, mid, end_, segments)
+ else
+ if next.on_curve? do
+ end_ = next
+ mid = stack
+ stack = nil
+ contour_segments_body(contour, length, i, stack, next, start, mid, end_, segments)
+ else
+ if stack != nil do
+ mid = stack
+ end_ = midpoint(stack, next)
+ stack = next
+ contour_segments_body(contour, length, i, stack, next, start, mid, end_, segments)
+ else
+ stack = next
+ contour_segments(contour, length, i, stack, next, start, mid, end_, segments)
+ end
+ end
+ end
+ end
+
+ defp contour_segments_body(contour, length, i, stack, next, start, mid, end_, segments) do
+ segment = %{a: start, b: mid, c: end_}
+ if next != nil do
+ start = end_
+ contour_segments(contour, length, i, stack, next, start, mid, end_, [segment | segments])
+ else
+ segments |> Enum.reverse()
+ end
+ end
+
+ defp contour_next_point(contour, length, i) do
+ if i < length do
+ {Enum.at(contour, i), i + 1}
+ else
+ {nil, i}
+ end
+ end
+
+ defp midpoint(p0, p1) do
+ %TTF.ControlPoint{x: (p0.x + p1.x) / 2.0,
+ y: (p0.y + p1.y) / 2.0,
+ on_curve?: true}
+ end
+
+end
diff --git a/lib/ttf/glyph/compound_contours.ex b/lib/ttf/glyph/compound_contours.ex
new file mode 100644
index 0000000..92da60f
--- /dev/null
+++ b/lib/ttf/glyph/compound_contours.ex
@@ -0,0 +1,6 @@
+defmodule TTF.Glyph.CompoundContours do
+
+ def parse(_glyph, _rest) do
+ raise ArgumentError, "not implemented"
+ end
+end
diff --git a/lib/ttf/glyph/simple_contours.ex b/lib/ttf/glyph/simple_contours.ex
new file mode 100644
index 0000000..36c8101
--- /dev/null
+++ b/lib/ttf/glyph/simple_contours.ex
@@ -0,0 +1,109 @@
+defmodule TTF.Glyph.SimpleContours do
+ use Bitwise
+
+ alias TTF.Glyph
+ alias TTF.ControlPoint
+
+ @on_curve 0x01
+ @x_short 0x02
+ @y_short 0x04
+ @repeat_flag 0x08
+ @x_same 0x10
+ @y_same 0x20
+
+ def parse(glyph, rest) do
+ {contour_endpoint_indexes, rest} = TTF.parse_ub16_array(rest, glyph.contour_count)
+ n_points = 1 + elem(contour_endpoint_indexes, glyph.contour_count - 1)
+ <<instruction_length::unsigned-big-16, _::binary-size(instruction_length), rest::binary>> = rest
+ glyph = %Glyph{glyph | contour_endpoint_indexes: contour_endpoint_indexes, n_points: n_points}
+ {glyph, rest} = flags(glyph, rest)
+ glyph
+ |> points(rest)
+ |> contours()
+ end
+
+ def flags(glyph, rest) do
+ flags(glyph, rest, 0, [])
+ end
+ def flags(glyph = %Glyph{n_points: n_points}, rest, contour, acc) when contour >= n_points do
+ flags = acc |> Enum.reverse() |> List.to_tuple()
+ {%Glyph{glyph | flags: flags}, rest}
+ end
+ def flags(glyph, <<byte, rest::binary>>, contour, acc) do
+ if band(@repeat_flag, byte) do
+ <<repeat_n::unsigned-8, rest::binary>> = rest
+ repeat = 0..(repeat_n - 1)
+ |> Enum.map(fn _ -> byte end)
+ flags(glyph, rest, contour + 1, repeat ++ acc)
+ else
+ flags(glyph, rest, contour + 1, [byte | acc])
+ end
+ end
+
+ def points(glyph, rest) do
+ {x_points, rest} = parse_points(glyph, rest, :x)
+ {y_points, _rest} = parse_points(glyph, rest, :y)
+ glyph
+ |> points(0, x_points, y_points, 0, 0, [])
+ end
+
+ def points(glyph = %Glyph{n_points: n_points}, i, _x_points, _y_points, _x, _y, acc) when i >= n_points do
+ control_points = acc |> Enum.reverse()
+ %Glyph{glyph | control_points: control_points}
+ end
+ def points(glyph = %Glyph{flags: flags}, i, x_points, y_points, x, y, acc) do
+ x_point = elem(x_points, i)
+ y_point = elem(y_points, i)
+ flag = elem(flags, i)
+ x = x + x_point
+ y = y + y_point
+ on_curve? = band(1, flag) != 0
+ acc = [%ControlPoint{x: x, y: y, on_curve?: on_curve?} | acc]
+ points(glyph, i + 1, x_points, y_points, x, y, acc)
+ end
+
+ def contours(glyph) do
+ contours(glyph, 0, [], 0)
+ end
+ def contours(glyph = %Glyph{contour_count: contour_count}, i, acc, _start) when i >= contour_count do
+ contours = acc |> Enum.reverse()
+ %Glyph{glyph | contours: contours}
+ end
+ def contours(glyph, i, acc, start) do
+ end_ = elem(glyph.contour_endpoint_indexes, i)
+ acc = [Enum.slice(glyph.control_points, start..end_) | acc]
+ contours(glyph, i + 1, acc, end_ + 1)
+ end
+
+ def parse_points(glyph, rest, axis) do
+ points = []
+ short_mask = if axis == :x, do: @x_short, else: @y_short
+ same_mask = if axis == :x, do: @x_same, else: @y_same
+ parse_points(glyph, rest, short_mask, same_mask, points, 0)
+ end
+
+ def parse_points(%Glyph{n_points: n_points}, rest, _short_mask, _same_mask, points, i) when i >= n_points do
+ points = points |> Enum.reverse() |> List.to_tuple()
+ {points, rest}
+ end
+ def parse_points(glyph = %Glyph{flags: flags}, rest, short_mask, same_mask, points, i) do
+ flag = elem(flags, i)
+ short? = band(short_mask, flag) != 0
+ same? = band(same_mask, flag) != 0
+ if short? do
+ <<new_point::unsigned-8, rest::binary>> = rest
+ p = if same?, do: new_point, else: -new_point
+ points = [p | points]
+ parse_points(glyph, rest, short_mask, same_mask, points, i + 1)
+ else
+ if same? do
+ points = [0 | points]
+ parse_points(glyph, rest, short_mask, same_mask, points, i + 1)
+ else
+ <<new_point::signed-big-16, rest::binary>> = rest
+ points = [new_point | points]
+ parse_points(glyph, rest, short_mask, same_mask, points, i + 1)
+ end
+ end
+ end
+end
diff --git a/src/gl_const.erl b/src/gl_const.erl
index 82c655a..4415535 100644
--- a/src/gl_const.erl
+++ b/src/gl_const.erl
@@ -5,9 +5,11 @@
gl_color_buffer_bit() -> ?GL_COLOR_BUFFER_BIT.
gl_compile() -> ?GL_COMPILE.
+gl_compile_and_execute() -> ?GL_COMPILE_AND_EXECUTE.
gl_depth_buffer_bit() -> ?GL_DEPTH_BUFFER_BIT.
gl_depth_test() -> ?GL_DEPTH_TEST.
gl_lequal() -> ?GL_LEQUAL.
+gl_line_loop() -> ?GL_LINE_LOOP.
gl_line_strip() -> ?GL_LINE_STRIP.
gl_modelview() -> ?GL_MODELVIEW.
gl_nicest() -> ?GL_NICEST.