Branch
Hash :
d95802fd
Author :
Thomas de Grivel
Date :
2022-02-12T18:01:48
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
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)
check_order!(contour_endpoint_indexes, 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
defp check_order!(_, size) when size < 2, do: :ok
defp check_order!(tuple, size) do
ordered? = 0..(size - 2)
|> Enum.all?(fn i ->
a = elem(tuple, i)
b = elem(tuple, i + 1)
a < b
end)
if ! ordered? do
raise ArgumentError, "tuple is not ordered: #{tuple}"
end
:ok
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) != 0 do
<<repeat_n::unsigned-8, rest::binary>> = rest
repeat = 1..repeat_n
|> 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