|
|
|
static u8
|
|
|
|
read_8(struct font_buffer *buf)
|
|
|
|
{
|
|
|
|
u8 result = buf->data[buf->offset];
|
|
|
|
buf->offset += 1;
|
|
|
|
return(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
static s8
|
|
|
|
read_8s(struct font_buffer *buf)
|
|
|
|
{
|
|
|
|
s8 result = buf->data[buf->offset];
|
|
|
|
buf->offset += 1;
|
|
|
|
return(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
static u16
|
|
|
|
be16(u8 *buf)
|
|
|
|
{
|
|
|
|
u16 result = buf[0] << 8 | buf[1];
|
|
|
|
return(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
static s16
|
|
|
|
be16s(u8 *buf)
|
|
|
|
{
|
|
|
|
s16 result = buf[0] << 8 | buf[1];
|
|
|
|
return(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
static u16
|
|
|
|
read_be16(struct font_buffer *buf)
|
|
|
|
{
|
|
|
|
u16 result = be16(buf->data + buf->offset);
|
|
|
|
buf->offset += 2;
|
|
|
|
return(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
static s16
|
|
|
|
read_be16s(struct font_buffer *buf)
|
|
|
|
{
|
|
|
|
s16 result = be16s(buf->data + buf->offset);
|
|
|
|
buf->offset += 2;
|
|
|
|
return(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
static u32
|
|
|
|
be32(u8 *buf)
|
|
|
|
{
|
|
|
|
u32 result = ((u32) buf[0] << 24) | ((u32) buf[1] << 16) | ((u32) buf[2] << 8) | ((u32) buf[3]);
|
|
|
|
return(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
static u32
|
|
|
|
read_be32(struct font_buffer *buf)
|
|
|
|
{
|
|
|
|
u32 result = be32(buf->data + buf->offset);
|
|
|
|
buf->offset += 4;
|
|
|
|
return(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
static f32
|
|
|
|
read_be214(struct font_buffer *buf)
|
|
|
|
{
|
|
|
|
f32 result = read_be16s(buf) / 16384.0f;
|
|
|
|
return(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct font_buffer
|
|
|
|
read_file(char *filename)
|
|
|
|
{
|
|
|
|
FILE *file = fopen(filename, "rb");
|
|
|
|
struct font_buffer result = { 0 };
|
|
|
|
|
|
|
|
ASSERT(file);
|
|
|
|
|
|
|
|
if (file) {
|
|
|
|
fseek(file, 0, SEEK_END);
|
|
|
|
result.size = ftell(file);
|
|
|
|
result.data = malloc(result.size);
|
|
|
|
fseek(file, 0, SEEK_SET);
|
|
|
|
fread(result.data, result.size, 1, file);
|
|
|
|
fclose(file);
|
|
|
|
}
|
|
|
|
|
|
|
|
return(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
static u32
|
|
|
|
get_glyph_index_format12(struct ttf_font *font, u16 codepoint)
|
|
|
|
{
|
|
|
|
u32 ngroups = be32(font->file.data + font->dir.cmap_offset + 12);
|
|
|
|
|
|
|
|
font->file.offset = font->dir.cmap_offset + 16;
|
|
|
|
|
|
|
|
// TODO: binsearch
|
|
|
|
for (u32 g = 0; g < ngroups; ++g) {
|
|
|
|
u32 start_charcode = read_be32(&font->file);
|
|
|
|
u32 end_charcode = read_be32(&font->file);
|
|
|
|
u32 start_glyphcode = read_be32(&font->file);
|
|
|
|
|
|
|
|
if (start_charcode <= codepoint && codepoint <= end_charcode) {
|
|
|
|
u32 glyph_index = start_glyphcode + (codepoint - start_charcode);
|
|
|
|
return(glyph_index);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (start_charcode > codepoint) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static s16
|
|
|
|
get_glyph_index_format4(struct ttf_font *font, u16 codepoint)
|
|
|
|
{
|
|
|
|
int segcount_x2 = be16(font->file.data + font->dir.cmap_offset + 6);
|
|
|
|
|
|
|
|
u16 *end_codes = (u16 *) (font->file.data + font->dir.cmap_offset + 16);
|
|
|
|
u16 *start_codes = (u16 *) (font->file.data + font->dir.cmap_offset + 16 + segcount_x2 + 2);
|
|
|
|
u16 *id_deltas = (u16 *) (font->file.data + font->dir.cmap_offset + 16 + segcount_x2 * 2 + 2);
|
|
|
|
u16 *id_range_offset = (u16 *) (font->file.data + font->dir.cmap_offset + 16 + segcount_x2 * 3 + 2);
|
|
|
|
|
|
|
|
int index = -1;
|
|
|
|
|
|
|
|
for (int i = 0; i < segcount_x2 / 2; ++i) {
|
|
|
|
u16 end_code = be16((u8 *) (end_codes + i));
|
|
|
|
if (end_code >= codepoint) {
|
|
|
|
index = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (index == -1) {
|
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
u16 start_code = be16((u8 *) (start_codes + index));
|
|
|
|
if (start_code <= codepoint) {
|
|
|
|
u16 ir_offset = be16((u8 *) (id_range_offset + index));
|
|
|
|
if (ir_offset != 0) {
|
|
|
|
u16 glyph_index = be16((u8 *) (id_range_offset
|
|
|
|
+ index + ir_offset / 2
|
|
|
|
+ (codepoint - start_code)));
|
|
|
|
|
|
|
|
if (!glyph_index) {
|
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
u16 id_delta = be16((u8 *) (id_deltas + index));
|
|
|
|
u16 result = (u16) (glyph_index + id_delta);
|
|
|
|
|
|
|
|
return(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
u16 id_delta = be16((u8 *) (id_deltas + index));
|
|
|
|
u16 result = (u16) (codepoint + id_delta);
|
|
|
|
|
|
|
|
return(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static u16
|
|
|
|
get_glyph_index_format6(struct ttf_font *font, u16 codepoint)
|
|
|
|
{
|
|
|
|
u16 first_code = be16(font->file.data + font->dir.cmap_offset + 6);
|
|
|
|
u16 entry_count = be16(font->file.data + font->dir.cmap_offset + 8);
|
|
|
|
u16 *glyph_index_array = (u16 *) (font->file.data + font->dir.cmap_offset + 10);
|
|
|
|
|
|
|
|
if (first_code <= codepoint && codepoint < first_code + entry_count) {
|
|
|
|
u16 glyph_index = be16((u8 *) (glyph_index_array + (codepoint - first_code)));
|
|
|
|
return(glyph_index);
|
|
|
|
}
|
|
|
|
|
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static u16
|
|
|
|
get_glyph_index_format0(struct ttf_font *font, u16 codepoint)
|
|
|
|
{
|
|
|
|
u8 *glyph_index_array = font->file.data + font->dir.cmap_offset + 6;
|
|
|
|
|
|
|
|
if (codepoint <= 0xFF) {
|
|
|
|
u8 glyph_index = glyph_index_array[codepoint];
|
|
|
|
return(glyph_index);
|
|
|
|
}
|
|
|
|
|
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static u32
|
|
|
|
get_glyph_index(struct ttf_font *font, u16 codepoint)
|
|
|
|
{
|
|
|
|
u32 result = 0;
|
|
|
|
|
|
|
|
switch (font->cmap_format) {
|
|
|
|
case 0: {
|
|
|
|
result = get_glyph_index_format0(font, codepoint);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 12: {
|
|
|
|
result = get_glyph_index_format12(font, codepoint);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 6: {
|
|
|
|
result = get_glyph_index_format6(font, codepoint);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 4: {
|
|
|
|
result = get_glyph_index_format4(font, codepoint);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
default: {
|
|
|
|
ASSERT(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
static u32
|
|
|
|
get_glyph_offset(struct ttf_font *font, u32 glyph_index)
|
|
|
|
{
|
|
|
|
u32 offset = 0;
|
|
|
|
u32 offset_next;
|
|
|
|
|
|
|
|
if (font->head.itl_format == 1) {
|
|
|
|
font->file.offset = font->dir.loca_offset + glyph_index * 4;
|
|
|
|
offset = read_be32(&font->file);
|
|
|
|
offset_next = read_be32(&font->file);
|
|
|
|
} else {
|
|
|
|
font->file.offset = font->dir.loca_offset + glyph_index * 2;
|
|
|
|
offset = read_be16(&font->file);
|
|
|
|
offset *= 2;
|
|
|
|
offset_next = read_be16(&font->file);
|
|
|
|
offset_next *= 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (offset == offset_next) {
|
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
return(offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
static s16
|
|
|
|
get_current_coordinate(struct font_buffer *font_file, int flag_combined)
|
|
|
|
{
|
|
|
|
s16 current_coordinate = 0;
|
|
|
|
|
|
|
|
switch (flag_combined) {
|
|
|
|
case 0: {
|
|
|
|
current_coordinate = read_be16(font_file);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 1: {
|
|
|
|
current_coordinate = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 2: {
|
|
|
|
current_coordinate = read_8(font_file);
|
|
|
|
current_coordinate *= -1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 3: {
|
|
|
|
current_coordinate = read_8(font_file);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return(current_coordinate);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline u32
|
|
|
|
pop(struct ttf_graphics_state *state)
|
|
|
|
{
|
|
|
|
u32 result = state->stack[state->stack_head - 1];
|
|
|
|
--state->stack_head;
|
|
|
|
return(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline f32
|
|
|
|
popf(struct ttf_graphics_state *state)
|
|
|
|
{
|
|
|
|
u32 integer = state->stack[state->stack_head - 1];
|
|
|
|
--state->stack_head;
|
|
|
|
f32 result;
|
|
|
|
memcpy(&result, &integer, sizeof(f32));
|
|
|
|
return(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
push(struct ttf_graphics_state *state, u32 value)
|
|
|
|
{
|
|
|
|
state->stack[state->stack_head] = value;
|
|
|
|
++state->stack_head;
|
|
|
|
}
|
|
|
|
|
|
|
|
static u32
|
|
|
|
skip_until(struct font_buffer *font_file, u32 start, u32 size, enum ttf_instruction until)
|
|
|
|
{
|
|
|
|
u32 result = -1; // -1 means not found
|
|
|
|
|
|
|
|
u32 base = start;
|
|
|
|
u32 offset = start;
|
|
|
|
|
|
|
|
while (offset - base < size) {
|
|
|
|
enum ttf_instruction inst = font_file->data[offset];
|
|
|
|
|
|
|
|
if (inst == until) {
|
|
|
|
result = offset;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
++offset;
|
|
|
|
|
|
|
|
// thankfully, ONLY push instructions take data from the instructin stream
|
|
|
|
if (inst == NPUSHB) {
|
|
|
|
u8 n = font_file->data[offset++];
|
|
|
|
offset += n;
|
|
|
|
} else if (inst == NPUSHW) {
|
|
|
|
u8 n = font_file->data[offset++];
|
|
|
|
offset += n * 2;
|
|
|
|
} else if (PUSHB <= inst && inst <= PUSHB_TOP) {
|
|
|
|
u8 n = inst - PUSHB + 1;
|
|
|
|
offset += n;
|
|
|
|
} else if (PUSHW <= inst && inst <= PUSHW_TOP) {
|
|
|
|
u8 n = inst - PUSHW + 1;
|
|
|
|
offset += n * 2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct v2u32
|
|
|
|
find_if_pair(struct font_buffer *font_file, u32 start, u32 size)
|
|
|
|
{
|
|
|
|
// Find matching ELSE and EIF for an IF, -1 means not found
|
|
|
|
struct v2u32 result = { -1, -1 };
|
|
|
|
|
|
|
|
u32 base = start;
|
|
|
|
u32 offset = start;
|
|
|
|
u32 depth = 1;
|
|
|
|
|
|
|
|
while (offset - base < size) {
|
|
|
|
enum ttf_instruction inst = font_file->data[offset++];
|
|
|
|
|
|
|
|
if (inst == NPUSHB) {
|
|
|
|
u8 n = font_file->data[offset++];
|
|
|
|
offset += n;
|
|
|
|
} else if (inst == NPUSHW) {
|
|
|
|
u8 n = font_file->data[offset++];
|
|
|
|
offset += n * 2;
|
|
|
|
} else if (PUSHB <= inst && inst <= PUSHB_TOP) {
|
|
|
|
u8 n = inst - PUSHB + 1;
|
|
|
|
offset += n;
|
|
|
|
} else if (PUSHW <= inst && inst <= PUSHW_TOP) {
|
|
|
|
u8 n = inst - PUSHW + 1;
|
|
|
|
offset += n * 2;
|
|
|
|
} else if (inst == IF) {
|
|
|
|
++depth;
|
|
|
|
} else if (inst == ELSE) {
|
|
|
|
if (depth == 1) {
|
|
|
|
result.x = offset;
|
|
|
|
}
|
|
|
|
} else if (inst == EIF) {
|
|
|
|
if (depth == 1) {
|
|
|
|
result.y = offset;
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
--depth;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
static s16
|
|
|
|
funits_to_pixels(u32 funits)
|
|
|
|
{
|
|
|
|
// TODO
|
|
|
|
return(funits);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
iterate_instructions(struct ttf_font *font, struct font_buffer *font_file, u32 offset, u32 size, struct ttf_graphics_state *state)
|
|
|
|
{
|
|
|
|
const struct v2f X_AXIS = { 1.0f, 0.0f };
|
|
|
|
const struct v2f Y_AXIS = { 0.0f, 1.0f };
|
|
|
|
|
|
|
|
u32 base_offset = offset;
|
|
|
|
u32 n_fdefs = 0;
|
|
|
|
|
|
|
|
int function_define = -1;
|
|
|
|
int if_true = 0;
|
|
|
|
|
|
|
|
while (offset - base_offset < size) {
|
|
|
|
enum ttf_instruction inst = font_file->data[offset++];
|
|
|
|
|
|
|
|
if (function_define >= 0 && inst != ENDF) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (offset == 13622) {
|
|
|
|
int __sdfsdf = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
printf("%6d: ", offset);
|
|
|
|
printf("%s\n", ttfe_instruction_name(inst));
|
|
|
|
#endif
|
|
|
|
|
|
|
|
switch (inst) {
|
|
|
|
case NPUSHB: {
|
|
|
|
u8 n = font_file->data[offset++];
|
|
|
|
for (int b = 0; b < n; ++b) {
|
|
|
|
u8 b = font_file->data[offset++];
|
|
|
|
push(state, b);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case NPUSHW: {
|
|
|
|
u8 n = font_file->data[offset++];
|
|
|
|
for (int b = 0; b < n; ++b) {
|
|
|
|
u16 word = be16(font_file->data + offset);
|
|
|
|
offset += 2;
|
|
|
|
push(state, word);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case PUSHB ... PUSHB_TOP: {
|
|
|
|
u8 overflow = inst - PUSHB;
|
|
|
|
int abc = overflow + 1;
|
|
|
|
|
|
|
|
for (int i = 0; i < abc; ++i) {
|
|
|
|
u8 b = font_file->data[offset++];
|
|
|
|
push(state, b);
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case PUSHW ... PUSHW_TOP: {
|
|
|
|
u8 overflow = inst - PUSHW;
|
|
|
|
int abc = overflow + 1;
|
|
|
|
|
|
|
|
for (int i = 0; i < abc; ++i) {
|
|
|
|
u16 word = be16(font_file->data + offset);
|
|
|
|
offset += 2;
|
|
|
|
push(state, word);
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case RS: {
|
|
|
|
u32 location = pop(state);
|
|
|
|
u32 value = font->storage[location];
|
|
|
|
push(state, value);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case WS: {
|
|
|
|
u32 value = pop(state);
|
|
|
|
u32 location = pop(state);
|
|
|
|
font->storage[location] = value;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case WCVTP: {
|
|
|
|
u32 value = pop(state);
|
|
|
|
u32 location = pop(state);
|
|
|
|
s16 cvt_value = value;
|
|
|
|
assert(location <= font->dir.cvt_size / sizeof(s16));
|
|
|
|
font->cvt[location] = cvt_value;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case WCVTF: {
|
|
|
|
u32 value = pop(state);
|
|
|
|
u32 location = pop(state);
|
|
|
|
s16 cvt_value = funits_to_pixels(value);
|
|
|
|
assert(location <= font->dir.cvt_size / sizeof(s16));
|
|
|
|
font->cvt[location] = cvt_value;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case RCVT: {
|
|
|
|
u32 location = pop(state);
|
|
|
|
assert(location <= font->dir.cvt_size / sizeof(s16));
|
|
|
|
s16 integer = font->cvt[location];
|
|
|
|
push(state, integer);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case SVTCA ... SVTCA_TOP: {
|
|
|
|
u8 overflow = inst - SVTCA;
|
|
|
|
int a = overflow;
|
|
|
|
|
|
|
|
if (a == 0) {
|
|
|
|
state->projection_vector = Y_AXIS;
|
|
|
|
state->freedom_vector = Y_AXIS;
|
|
|
|
} else if (a == 1) {
|
|
|
|
state->projection_vector = X_AXIS;
|
|
|
|
state->freedom_vector = X_AXIS;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case SPVTCA ... SPVTCA_TOP: {
|
|
|
|
u8 overflow = inst - SPVTCA;
|
|
|
|
int a = overflow;
|
|
|
|
|
|
|
|
if (a == 0) {
|
|
|
|
state->projection_vector = Y_AXIS;
|
|
|
|
} else if (a == 1) {
|
|
|
|
state->projection_vector = X_AXIS;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case SFVTCA ... SFVTCA_TOP: {
|
|
|
|
u8 overflow = inst - SFVTCA;
|
|
|
|
int a = overflow;
|
|
|
|
|
|
|
|
if (a == 0) {
|
|
|
|
state->freedom_vector = Y_AXIS;
|
|
|
|
} else if (a == 1) {
|
|
|
|
state->freedom_vector = X_AXIS;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case SPVTL ... SPVTL_TOP: {
|
|
|
|
u8 overflow = inst - SFVTCA;
|
|
|
|
int a = overflow;
|
|
|
|
|
|
|
|
u32 p1 = pop(state);
|
|
|
|
u32 p2 = pop(state);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case SFVTL ... SFVTL_TOP: {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case SFVTPV: {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case SDPVTL ... SDPVTL_TOP: {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case SPVFS: {
|
|
|
|
u32 y = pop(state);
|
|
|
|
u32 x = pop(state);
|
|
|
|
// TODO: convert to 2.14 fixed point
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case SFVFS: {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case GPV: {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case GFV: {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case SRP0: {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case SRP1: {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case SRP2: {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case SZP0: {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case SZP1: {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case SZP2: {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case SZPS: {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case RTHG: {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case RTG: {
|
|
|
|
state->round_state = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case RTDG: {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case RDTG: {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case RUTG: {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case ROFF: {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case SROUND: {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case S45ROUND: {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case SLOOP: {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case SMD: {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case INSTCTRL: {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case SCANCTRL: {
|
|
|
|
// TODO: turn dropout control (state->scan_control) on or off
|
|
|
|
u32 n = pop(state);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case SCANTYPE: {
|
|
|
|
// TODO: dropout control mode
|
|
|
|
u32 val = pop(state);
|
|
|
|
u16 n = (u16) val;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case SCVTCI: {
|
|
|
|
u32 cut_in_64th = pop(state);
|
|
|
|
f32 cut_in = cut_in_64th / 64.0f;
|
|
|
|
state->control_value_cut_in = cut_in;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case SSWCI: {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case SSW: {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case FLIPON: {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case FLIPOFF: {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case SANGW: {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case SDB: {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case SDS: {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case GC_ ... GC_TOP: {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case SCFS: {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case MD ... MD_TOP: {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case MPPEM: {
|
|
|
|
/* (funit -> px) scale = pointSize * resolution / ( 72 points per inch * units_per_em ) */
|
|
|
|
/* 1em = units_per_em funits */
|
|
|
|
/* 1em = scale * units_per_em pixels */
|
|
|
|
|
|
|
|
u32 dpi = 96; // TODO: query system
|
|
|
|
u32 point_size = 12; // TODO: pass as parameter
|
|
|
|
u16 ppem = point_size * dpi / 72;
|
|
|
|
|
|
|
|
push(state, ppem);
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case MPS: {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case FLIPPT: {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case FLIPRGON: {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case FLIPRGOFF: {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case SHP ... SHP_TOP: {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case SHC ... SHC_TOP: {
|
|
|
|
u8 overflow = inst - SHC;
|
|
|
|
u32 c = pop(state);
|
|
|
|
// TODO
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case SHZ ... SHZ_TOP: {
|
|
|
|
u8 overflow = inst - SHZ;
|
|
|
|
u32 e = pop(state);
|
|
|
|
// TODO
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case SHPIX: {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case MSIRP ... MSIRP_TOP: {
|
|
|
|
u8 overflow = inst - MSIRP;
|
|
|
|
f32 d = popf(state);
|
|
|
|
u32 p = pop(state);
|
|
|
|
// TODO
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case MDAP ... MDAP_TOP: {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case MIAP ... MIAP_TOP: {
|
|
|
|
// 0: don’t round the distance and don’t look at the control_value_cut_in
|
|
|
|
// 1: round the distance and look at the control_value_cut_in
|
|
|
|
int a = inst - MIAP;
|
|
|
|
|
|
|
|
assert(state->stack_head >= 2);
|
|
|
|
|
|
|
|
u32 n = pop(state); // CVT entry number
|
|
|
|
u32 p = pop(state); // point number
|
|
|
|
|
|
|
|
// TODO
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case MDRP ... MDRP_TOP: {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case MIRP ... MIRP_TOP: {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case ALIGNRP: {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case ISECT: {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case ALIGNPTS: {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case IP: {
|
|
|
|
u32 p = pop(state);
|
|
|
|
// TODO
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case UTP: {
|
|
|
|
u32 p = pop(state);
|
|
|
|
// TODO
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case IUP ... IUP_TOP: {
|
|
|
|
u8 overflow = inst - IUP;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DELTAP1: {
|
|
|
|
u32 n = pop(state);
|
|
|
|
for (u32 i = 0; i < n; ++i) {
|
|
|
|
u32 pi = pop(state);
|
|
|
|
u32 argi = pop(state);
|
|
|
|
// TODO
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DELTAP2: {
|
|
|
|
u32 n = pop(state);
|
|
|
|
for (u32 i = 0; i < n; ++i) {
|
|
|
|
u32 pi = pop(state);
|
|
|
|
u32 argi = pop(state);
|
|
|
|
// TODO
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DELTAP3: {
|
|
|
|
u32 n = pop(state);
|
|
|
|
for (u32 i = 0; i < n; ++i) {
|
|
|
|
u32 pi = pop(state);
|
|
|
|
u32 argi = pop(state);
|
|
|
|
// TODO
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DELTAC1: {
|
|
|
|
u32 n = pop(state);
|
|
|
|
for (u32 i = 0; i < n; ++i) {
|
|
|
|
u32 ci = pop(state);
|
|
|
|
u32 argi = pop(state);
|
|
|
|
// TODO
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DELTAC2: {
|
|
|
|
u32 n = pop(state);
|
|
|
|
for (u32 i = 0; i < n; ++i) {
|
|
|
|
u32 ci = pop(state);
|
|
|
|
u32 argi = pop(state);
|
|
|
|
// TODO
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DELTAC3: {
|
|
|
|
u32 n = pop(state);
|
|
|
|
for (u32 i = 0; i < n; ++i) {
|
|
|
|
u32 ci = pop(state);
|
|
|
|
u32 argi = pop(state);
|
|
|
|
// TODO
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DUP: {
|
|
|
|
u32 e = pop(state);
|
|
|
|
push(state, e);
|
|
|
|
push(state, e);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case POP: {
|
|
|
|
--state->stack_head;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case CLEAR: {
|
|
|
|
state->stack_head = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case SWAP: {
|
|
|
|
assert(state->stack_head >= 2);
|
|
|
|
|
|
|
|
u32 last = state->stack[state->stack_head - 1];
|
|
|
|
u32 prelast = state->stack[state->stack_head - 2];
|
|
|
|
|
|
|
|
state->stack[state->stack_head - 2] = last;
|
|
|
|
state->stack[state->stack_head - 1] = prelast;
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DEPTH: {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case CINDEX: {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case MINDEX: {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case ROLL: {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case IF: {
|
|
|
|
struct v2u32 pair = find_if_pair(font_file, offset, size - (offset - base_offset));
|
|
|
|
|
|
|
|
if_true = pop(state);
|
|
|
|
|
|
|
|
if (!if_true) {
|
|
|
|
assert(pair.x != (u32) -1 || pair.y != (u32) -1);
|
|
|
|
offset = MIN(pair.x, pair.y);
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case ELSE: {
|
|
|
|
if (if_true) {
|
|
|
|
offset = skip_until(font_file, offset, size - (offset - base_offset), EIF);
|
|
|
|
assert(offset != (u32) -1);
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case EIF: {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case JROT: {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case JMPR: {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case JROF: {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case LT: {
|
|
|
|
u32 e2 = pop(state);
|
|
|
|
u32 e1 = pop(state);
|
|
|
|
u32 less = (e1 < e2 ? 1 : 0);
|
|
|
|
push(state, less);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case LTEQ: {
|
|
|
|
u32 e2 = pop(state);
|
|
|
|
u32 e1 = pop(state);
|
|
|
|
u32 leq = (e1 <= e2 ? 1 : 0);
|
|
|
|
push(state, leq);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case GT: {
|
|
|
|
u32 e2 = pop(state);
|
|
|
|
u32 e1 = pop(state);
|
|
|
|
u32 gt = (e1 > e2 ? 1 : 0);
|
|
|
|
push(state, gt);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case GTEQ: {
|
|
|
|
u32 e2 = pop(state);
|
|
|
|
u32 e1 = pop(state);
|
|
|
|
u32 geq = (e1 >= e2 ? 1 : 0);
|
|
|
|
push(state, geq);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case EQ: {
|
|
|
|
u32 e2 = pop(state);
|
|
|
|
u32 e1 = pop(state);
|
|
|
|
u32 equal = (e1 == e2 ? 1 : 0);
|
|
|
|
push(state, equal);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case NEQ: {
|
|
|
|
u32 e2 = pop(state);
|
|
|
|
u32 e1 = pop(state);
|
|
|
|
u32 neq = (e1 != e2 ? 1 : 0);
|
|
|
|
push(state, neq);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case ODD: {
|
|
|
|
u32 e1 = pop(state);
|
|
|
|
u32 odd = e1 & 1;
|
|
|
|
push(state, odd);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case EVEN: {
|
|
|
|
u32 e1 = pop(state);
|
|
|
|
u32 even = 1 - (e1 & 1);
|
|
|
|
push(state, even);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case AND: {
|
|
|
|
u32 e1 = pop(state);
|
|
|
|
u32 e2 = pop(state);
|
|
|
|
u32 and = (e1 & e2 ? 1 : 0);
|
|
|
|
push(state, and);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case OR: {
|
|
|
|
u32 e1 = pop(state);
|
|
|
|
u32 e2 = pop(state);
|
|
|
|
u32 or = (e1 | e2 ? 1 : 0);
|
|
|
|
push(state, or);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case NOT: {
|
|
|
|
u32 e1 = pop(state);
|
|
|
|
u32 not = (e1 ? 0 : 1);
|
|
|
|
push(state, not);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case ADD: {
|
|
|
|
f32 n1 = popf(state);
|
|
|
|
f32 n2 = popf(state);
|
|
|
|
f32 add = n2 + n1;
|
|
|
|
push(state, add);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case SUB: {
|
|
|
|
f32 n1 = popf(state);
|
|
|
|
f32 n2 = popf(state);
|
|
|
|
f32 sub = n2 - n1;
|
|
|
|
push(state, sub);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DIV: {
|
|
|
|
f32 n1 = popf(state);
|
|
|
|
f32 n2 = popf(state);
|
|
|
|
f32 div = n2 / n1;
|
|
|
|
push(state, div);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case MUL: {
|
|
|
|
f32 n1 = popf(state);
|
|
|
|
f32 n2 = popf(state);
|
|
|
|
f32 mul = n2 * n1;
|
|
|
|
push(state, mul);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case ABS: {
|
|
|
|
f32 n1 = popf(state);
|
|
|
|
f32 abs = fabsf(n1);
|
|
|
|
push(state, abs);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case NEG: {
|
|
|
|
f32 n1 = popf(state);
|
|
|
|
f32 neg = -n1;
|
|
|
|
push(state, neg);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case FLOOR: {
|
|
|
|
f32 n1 = popf(state);
|
|
|
|
f32 floor = floorf(n1);
|
|
|
|
push(state, floor);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case CEILING: {
|
|
|
|
f32 n1 = popf(state);
|
|
|
|
f32 ceil = ceilf(n1);
|
|
|
|
push(state, ceil);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case MAX: {
|
|
|
|
u32 e1 = pop(state);
|
|
|
|
u32 e2 = pop(state);
|
|
|
|
u32 max = (e1 > e2 ? e1 : e2);
|
|
|
|
push(state, max);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case MIN: {
|
|
|
|
u32 e1 = pop(state);
|
|
|
|
u32 e2 = pop(state);
|
|
|
|
u32 min = (e1 < e2 ? e1 : e2);
|
|
|
|
push(state, min);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case ROUND ... ROUND_TOP: {
|
|
|
|
u8 overflow = inst - ROUND;
|
|
|
|
u32 n1 = pop(state);
|
|
|
|
// TODO: do rounding
|
|
|
|
push(state, n1);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case NROUND ... NROUND_TOP: {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case FDEF: {
|
|
|
|
int f = state->stack[--state->stack_head];
|
|
|
|
font->functions[f].from = offset;
|
|
|
|
function_define = f;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case ENDF: {
|
|
|
|
font->functions[function_define].to = offset - 1; // NOTE: do not include ENDF in subproc
|
|
|
|
function_define = -1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case CALL: {
|
|
|
|
int f = state->stack[--state->stack_head];
|
|
|
|
assert(f <= font->maxp.max_fdefs);
|
|
|
|
struct ttf_function proc = font->functions[f];
|
|
|
|
iterate_instructions(font, font_file, proc.from, proc.to - proc.from, state);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case LOOPCALL: {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case IDEF: {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DEBUG: {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case GETINFO: {
|
|
|
|
// TODO?
|
|
|
|
int selector = pop(state);
|
|
|
|
push(state, 0);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case GETVARIATION: {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
default: {
|
|
|
|
printf("Unknown ttf instruction %x\n", inst);
|
|
|
|
__builtin_trap();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
get_simple_glyph_points(struct font_buffer *font_file, u16 number_of_countours, struct glyph *result)
|
|
|
|
{
|
|
|
|
u16 *end_pts_of_contours = calloc(1, number_of_countours * sizeof(u16));
|
|
|
|
for (int c = 0; c < number_of_countours; ++c) {
|
|
|
|
end_pts_of_contours[c] = read_be16(font_file);
|
|
|
|
}
|
|
|
|
|
|
|
|
// NOTE: skip instructions
|
|
|
|
u16 instruction_length = read_be16(font_file);
|
|
|
|
struct ttf_graphics_state glyph_state = default_graphics_state();
|
|
|
|
//iterate_instructions(font, font_file, font_file->offset, instruction_length, &glyph_state);
|
|
|
|
font_file->offset += instruction_length;
|
|
|
|
|
|
|
|
int last_index = end_pts_of_contours[number_of_countours - 1];
|
|
|
|
union glyph_flag *flags = calloc(1, last_index + 1);
|
|
|
|
struct glyph_point *points = malloc((last_index + 2) * 2 * sizeof(struct v2));
|
|
|
|
|
|
|
|
// NOTE: so that we can insert one point at the start if needed
|
|
|
|
// points = points + 1;
|
|
|
|
|
|
|
|
for (int i = 0; i < last_index + 1; ++i) {
|
|
|
|
flags[i].flag = read_8(font_file);
|
|
|
|
if (flags[i].repeat) {
|
|
|
|
u8 repeat_count = read_8(font_file);
|
|
|
|
while (repeat_count-- > 0) {
|
|
|
|
i++;
|
|
|
|
flags[i] = flags[i - 1];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
s16 prev_coordinate = 0;
|
|
|
|
int start = 0;
|
|
|
|
int head = 0;
|
|
|
|
|
|
|
|
for (int c = 0; c < number_of_countours; ++c) {
|
|
|
|
int end = end_pts_of_contours[c];
|
|
|
|
struct glyph_point first_point = { 0 };
|
|
|
|
|
|
|
|
for (int i = start; i < end + 1; ++i) {
|
|
|
|
// NOTE: read x coordinates
|
|
|
|
int flag_combined = (flags[i].x_short << 1) | flags[i].x_short_pos;
|
|
|
|
s16 current_coordinate = get_current_coordinate(font_file, flag_combined);
|
|
|
|
s16 read_x = current_coordinate + prev_coordinate;
|
|
|
|
|
|
|
|
int prev = i - 1;
|
|
|
|
if (prev >= start) {
|
|
|
|
//prev = end;
|
|
|
|
|
|
|
|
if (!flags[i].on_curve && !flags[prev].on_curve) {
|
|
|
|
// NOTE: implicit on-curve point
|
|
|
|
points[head].on_curve = true;
|
|
|
|
points[head].x = (read_x + points[head - 1].x) / 2;
|
|
|
|
++head;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
points[head].on_curve = flags[i].on_curve;
|
|
|
|
points[head].x = read_x;
|
|
|
|
++head;
|
|
|
|
|
|
|
|
if (i == start) {
|
|
|
|
// NOTE: first iteration could not have inserted an implicit point
|
|
|
|
first_point = points[head - 1];
|
|
|
|
}
|
|
|
|
|
|
|
|
prev_coordinate = read_x;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!flags[start].on_curve && !flags[end].on_curve) {
|
|
|
|
points[head].on_curve = true;
|
|
|
|
points[head].x = (first_point.x + points[head - 1].x) / 2;
|
|
|
|
++head;
|
|
|
|
}
|
|
|
|
|
|
|
|
start = end + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
prev_coordinate = 0;
|
|
|
|
start = 0;
|
|
|
|
head = 0;
|
|
|
|
|
|
|
|
int added_points = 0;
|
|
|
|
|
|
|
|
for (int c = 0; c < number_of_countours; ++c) {
|
|
|
|
int end = end_pts_of_contours[c];
|
|
|
|
struct glyph_point first_point = { 0 };
|
|
|
|
|
|
|
|
for (int i = start; i < end + 1; ++i) {
|
|
|
|
// NOTE: read y coordinates
|
|
|
|
int flag_combined = (flags[i].y_short << 1) | flags[i].y_short_pos;
|
|
|
|
s16 current_coordinate = get_current_coordinate(font_file, flag_combined);
|
|
|
|
s16 read_y = current_coordinate + prev_coordinate;
|
|
|
|
|
|
|
|
int prev = i - 1;
|
|
|
|
if (prev >= start) {
|
|
|
|
//prev = end;
|
|
|
|
|
|
|
|
if (!flags[i].on_curve && !flags[prev].on_curve) {
|
|
|
|
// NOTE: implicit on-curve point
|
|
|
|
points[head].on_curve = true;
|
|
|
|
points[head].y = (read_y + points[head - 1].y) / 2;
|
|
|
|
++head;
|
|
|
|
++added_points;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
points[head].on_curve = flags[i].on_curve;
|
|
|
|
points[head].y = read_y;
|
|
|
|
++head;
|
|
|
|
|
|
|
|
prev_coordinate = read_y;
|
|
|
|
|
|
|
|
if (i == start) {
|
|
|
|
// NOTE: first iteration could not have inserted an implicit point
|
|
|
|
first_point = points[head - 1];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!flags[start].on_curve && !flags[end].on_curve) {
|
|
|
|
points[head].on_curve = true;
|
|
|
|
points[head].y = (first_point.y + points[head - 1].y) / 2;
|
|
|
|
++head;
|
|
|
|
++added_points;
|
|
|
|
}
|
|
|
|
|
|
|
|
start = end + 1;
|
|
|
|
end_pts_of_contours[c] += added_points;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
result->points = points;
|
|
|
|
result->ncontours = number_of_countours;
|
|
|
|
result->end_pts_of_contours = end_pts_of_contours;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct glyph
|
|
|
|
get_glyph_outline(struct ttf_font *font, u32 glyph_index)
|
|
|
|
{
|
|
|
|
u32 glyph_offset = get_glyph_offset(font, glyph_index);
|
|
|
|
struct font_buffer *font_file = &font->file;
|
|
|
|
|
|
|
|
font_file->offset = font->dir.glyf_offset + glyph_offset;
|
|
|
|
|
|
|
|
struct glyph result = { 0 };
|
|
|
|
s16 number_of_countours = read_be16(font_file);
|
|
|
|
|
|
|
|
result.xmin = read_be16(font_file);
|
|
|
|
result.ymin = read_be16(font_file);
|
|
|
|
result.xmax = read_be16(font_file);
|
|
|
|
result.ymax = read_be16(font_file);
|
|
|
|
|
|
|
|
if (number_of_countours > 0) {
|
|
|
|
// NOTE: simple glyph
|
|
|
|
get_simple_glyph_points(font_file, number_of_countours, &result);
|
|
|
|
} else if (number_of_countours < 0) {
|
|
|
|
// NOTE: compund glyph
|
|
|
|
int head = 0;
|
|
|
|
|
|
|
|
result.end_pts_of_contours = malloc(font->maxp.max_component_contours * sizeof(u16));
|
|
|
|
result.points = malloc(font->maxp.max_component_points * 2 * sizeof(struct glyph_point));
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
u16 flags = read_be16(font_file);
|
|
|
|
u16 component_index = read_be16(font_file);
|
|
|
|
|
|
|
|
f32 matrix[6] = {1, 0, 0, 1, 0, 0}; /* NOTE(aolo2): matrix stuff is highjacked from stb */
|
|
|
|
|
|
|
|
if (flags & ARG_1_AND_2_ARE_WORDS) {
|
|
|
|
matrix[4] = read_be16(font_file);
|
|
|
|
matrix[5] = read_be16(font_file);
|
|
|
|
} else {
|
|
|
|
matrix[4] = read_8s(font_file);
|
|
|
|
matrix[5] = read_8s(font_file);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (flags & WE_HAVE_A_SCALE) {
|
|
|
|
f32 scale = read_be214(font_file);
|
|
|
|
matrix[0] = scale;
|
|
|
|
matrix[1] = 0;
|
|
|
|
matrix[2] = 0;
|
|
|
|
matrix[3] = scale;
|
|
|
|
} else if (flags & WE_HAVE_AN_X_AND_Y_SCALE ) {
|
|
|
|
f32 xscale = read_be214(font_file);
|
|
|
|
f32 yscale = read_be214(font_file);
|
|
|
|
matrix[0] = xscale;
|
|
|
|
matrix[1] = 0;
|
|
|
|
matrix[2] = 0;
|
|
|
|
matrix[3] = yscale;
|
|
|
|
} else if (flags & WE_HAVE_A_TWO_BY_TWO) {
|
|
|
|
f32 xscale = read_be214(font_file);
|
|
|
|
f32 scale01 = read_be214(font_file);
|
|
|
|
f32 scale10 = read_be214(font_file);
|
|
|
|
f32 yscale = read_be214(font_file);
|
|
|
|
matrix[0] = xscale;
|
|
|
|
matrix[1] = scale01;
|
|
|
|
matrix[2] = scale10;
|
|
|
|
matrix[3] = yscale;
|
|
|
|
}
|
|
|
|
|
|
|
|
f32 m = sqrtf(matrix[0] * matrix[0] + matrix[1] * matrix[1]);
|
|
|
|
f32 n = sqrtf(matrix[2] * matrix[2] + matrix[3] * matrix[3]);
|
|
|
|
|
|
|
|
u32 saved_offset = font_file->offset;
|
|
|
|
struct glyph component = get_glyph_outline(font, component_index);
|
|
|
|
font_file->offset = saved_offset;
|
|
|
|
|
|
|
|
int startv = 0;
|
|
|
|
for (int c = 0; c < component.ncontours; ++c) {
|
|
|
|
int endv = component.end_pts_of_contours[c] + 1;
|
|
|
|
|
|
|
|
for (int v = startv; v < endv; ++v) {
|
|
|
|
s16 x = component.points[v].x;
|
|
|
|
s16 y = component.points[v].y;
|
|
|
|
|
|
|
|
result.points[head].x = m * (matrix[0] * x + matrix[2] * y + matrix[4]);
|
|
|
|
result.points[head].y = n * (matrix[1] * x + matrix[3] * y + matrix[5]);
|
|
|
|
result.points[head].on_curve = component.points[v].on_curve;
|
|
|
|
++head;
|
|
|
|
}
|
|
|
|
|
|
|
|
startv = endv;
|
|
|
|
result.end_pts_of_contours[result.ncontours] = head - 1;
|
|
|
|
++result.ncontours;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(flags & MORE_COMPONENTS)) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// NOTE: space
|
|
|
|
//advance = 0; // TODO
|
|
|
|
}
|
|
|
|
|
|
|
|
return(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
get_hmtx(struct ttf_font *font, u32 glyph_index, struct glyph *dest)
|
|
|
|
{
|
|
|
|
struct font_buffer font_file = font->file;
|
|
|
|
|
|
|
|
if (font->is_monospace) {
|
|
|
|
font_file.offset = font->dir.hmtx_offset;
|
|
|
|
} else {
|
|
|
|
font_file.offset = font->dir.hmtx_offset + glyph_index * 4;
|
|
|
|
}
|
|
|
|
|
|
|
|
dest->advance = read_be16(&font_file);
|
|
|
|
dest->lsb = read_be16s(&font_file);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
get_string_width(struct ttf_font *font, int px_size, wchar_t *string, int length)
|
|
|
|
{
|
|
|
|
int result = 0;
|
|
|
|
struct glyph g = { 0 };
|
|
|
|
f32 scale = (f32) px_size / ((f32) (font->hhea.ascent - font->hhea.descent));
|
|
|
|
|
|
|
|
for (int i = 0; i < length; ++i) {
|
|
|
|
u16 codepoint = string[i];
|
|
|
|
u32 glyph_index = get_glyph_index(font, codepoint);
|
|
|
|
get_hmtx(font, glyph_index, &g);
|
|
|
|
result += ceil_f32(scale * g.advance); // NOTE(aolo2): ceil each time because that's what the rasterizing code does
|
|
|
|
}
|
|
|
|
|
|
|
|
return(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct glyph
|
|
|
|
get_outline(struct ttf_font *font, u16 codepoint)
|
|
|
|
{
|
|
|
|
u32 glyph_index = get_glyph_index(font, codepoint);
|
|
|
|
|
|
|
|
struct glyph result = get_glyph_outline(font, glyph_index);
|
|
|
|
get_hmtx(font, glyph_index, &result);
|
|
|
|
|
|
|
|
int xmin = result.xmin;
|
|
|
|
int ymin = result.ymin;
|
|
|
|
//int ymin = result.ymin;
|
|
|
|
|
|
|
|
for (int p = 0; p < result.end_pts_of_contours[result.ncontours - 1] + 1; ++p) {
|
|
|
|
struct glyph_point *gp = result.points + p;
|
|
|
|
gp->x -= xmin;
|
|
|
|
gp->y -= ymin;
|
|
|
|
}
|
|
|
|
|
|
|
|
result.baseline = - (int) result.ymin;
|
|
|
|
result.xmin = 0;
|
|
|
|
result.xmax -= xmin;
|
|
|
|
result.ymin = 0;
|
|
|
|
result.ymax -= result.ymin;
|
|
|
|
|
|
|
|
return(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
static u16
|
|
|
|
read_offset_table(struct font_buffer *file)
|
|
|
|
{
|
|
|
|
u16 num_tables = be16(file->data + 4);
|
|
|
|
return(num_tables);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct font_directory
|
|
|
|
read_font_directory(struct font_buffer *file)
|
|
|
|
{
|
|
|
|
struct font_directory result = { 0 };
|
|
|
|
u16 table_count = read_offset_table(file);
|
|
|
|
|
|
|
|
for (int i = 0; i < table_count; ++i) {
|
|
|
|
u32 tag = be32(file->data + 12 + 16 * i + 0);
|
|
|
|
u32 offset = be32(file->data + 12 + 16 * i + 8);
|
|
|
|
u32 length = be32(file->data + 12 + 16 * i + 12);
|
|
|
|
|
|
|
|
switch (tag) {
|
|
|
|
case 0x636d6170: {
|
|
|
|
/* cmap */
|
|
|
|
result.cmap_offset = offset;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 0x63767420: {
|
|
|
|
/* cvt */
|
|
|
|
result.cvt_offset = offset;
|
|
|
|
result.cvt_size = length;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 0x70726570: {
|
|
|
|
/* prep */
|
|
|
|
result.prep_offset = offset;
|
|
|
|
result.prep_size = length;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 0x6670676d: {
|
|
|
|
/* fpgm */
|
|
|
|
result.fpgm_offset = offset;
|
|
|
|
result.fpgm_size = length;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 0x6c6f6361: {
|
|
|
|
/* loca */
|
|
|
|
result.loca_offset = offset;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 0x68656164: {
|
|
|
|
/* head */
|
|
|
|
result.head_offset = offset;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 0x676c7966: {
|
|
|
|
/* glyf */
|
|
|
|
result.glyf_offset = offset;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 0x686d7478: {
|
|
|
|
/* hmtx */
|
|
|
|
result.hmtx_offset = offset;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 0x6d617870: {
|
|
|
|
/* maxp */
|
|
|
|
result.maxp_offset = offset;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 0x68686561: {
|
|
|
|
/* hhea */
|
|
|
|
result.hhea_offset = offset;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 0x706f7374: {
|
|
|
|
/* post */
|
|
|
|
result.post_offset = offset;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
read_head(struct font_directory font_dir, struct ttf_font *font)
|
|
|
|
{
|
|
|
|
font->head.units_per_em = be16(font->file.data + font_dir.head_offset + 18);
|
|
|
|
font->head.itl_format = be16(font->file.data + font_dir.head_offset + 50);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
read_hhea(struct font_directory font_dir, struct ttf_font *font)
|
|
|
|
{
|
|
|
|
font->hhea.ascent = be16s(font->file.data + font_dir.hhea_offset + 4);
|
|
|
|
font->hhea.descent = be16s(font->file.data + font_dir.hhea_offset + 6);
|
|
|
|
font->hhea.line_gap = be16s(font->file.data + font_dir.hhea_offset + 10);
|
|
|
|
font->hhea.max_advance = be16(font->file.data + font_dir.hhea_offset + 14);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
read_cmap(struct font_directory font_dir, struct ttf_font *font)
|
|
|
|
{
|
|
|
|
font->file.offset = font_dir.cmap_offset + 2;
|
|
|
|
|
|
|
|
u16 num_tables = read_be16(&font->file);
|
|
|
|
|
|
|
|
for (int st = 0; st < num_tables; ++st) {
|
|
|
|
u16 platform_id = read_be16(&font->file);
|
|
|
|
u16 platform_specific_id = read_be16(&font->file);
|
|
|
|
u32 subtable_offset = read_be32(&font->file);
|
|
|
|
|
|
|
|
if ((platform_id == 0 && (platform_specific_id == 3 || platform_specific_id == 4)) ||
|
|
|
|
(platform_id == 3 && ((platform_specific_id == 1 || platform_specific_id == 10))))
|
|
|
|
{
|
|
|
|
font->cmap_format = be16(font->file.data + font_dir.cmap_offset + subtable_offset);
|
|
|
|
font->dir.cmap_offset = font_dir.cmap_offset + subtable_offset;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
read_maxp(struct font_directory font_dir, struct ttf_font *font)
|
|
|
|
{
|
|
|
|
font->maxp.max_component_points = be16(font->file.data + font_dir.maxp_offset + 10);
|
|
|
|
font->maxp.max_component_contours = be16(font->file.data + font_dir.maxp_offset + 12);
|
|
|
|
font->maxp.max_storage = be16(font->file.data + font_dir.maxp_offset + 18);
|
|
|
|
font->maxp.max_fdefs = be16(font->file.data + font_dir.maxp_offset + 20);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
read_fpgm(struct ttf_font *font, struct font_buffer *font_file, struct font_directory font_dir, struct ttf_graphics_state *state)
|
|
|
|
{
|
|
|
|
iterate_instructions(font, font_file, font_dir.fpgm_offset, font_dir.fpgm_size, state);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
read_cvt(struct ttf_font *font, struct font_buffer *font_file, struct font_directory font_dir, struct ttf_graphics_state *state)
|
|
|
|
{
|
|
|
|
iterate_instructions(font, font_file, font_dir.prep_offset, font_dir.prep_size, state);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
read_post(struct font_directory font_dir, struct ttf_font *font)
|
|
|
|
{
|
|
|
|
if (font_dir.post_offset) {
|
|
|
|
font->is_monospace = be32(font->file.data + font_dir.post_offset + 12);
|
|
|
|
} else {
|
|
|
|
printf("Warning: font file doesn't have a 'post' table. Assuming font is proportional\n");
|
|
|
|
font->is_monospace = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct ttf_font
|
|
|
|
parse_ttf_file(char *filename, char *fontname)
|
|
|
|
{
|
|
|
|
struct ttf_font result = { 0 };
|
|
|
|
struct font_buffer font_file = read_file(filename);
|
|
|
|
struct font_directory font_dir = read_font_directory(&font_file);
|
|
|
|
|
|
|
|
result.file = font_file;
|
|
|
|
result.dir = font_dir;
|
|
|
|
result.dir.glyf_offset = font_dir.glyf_offset;
|
|
|
|
result.dir.loca_offset = font_dir.loca_offset;
|
|
|
|
result.dir.hmtx_offset = font_dir.hmtx_offset;
|
|
|
|
result.cvt = (s16 *) (font_file.data + font_dir.cvt_offset);
|
|
|
|
|
|
|
|
read_head(font_dir, &result);
|
|
|
|
read_post(font_dir, &result);
|
|
|
|
read_maxp(font_dir, &result);
|
|
|
|
read_cmap(font_dir, &result);
|
|
|
|
read_hhea(font_dir, &result);
|
|
|
|
|
|
|
|
if (result.maxp.max_fdefs) {
|
|
|
|
result.functions = malloc(result.maxp.max_fdefs * sizeof(struct ttf_function));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (result.maxp.max_storage) {
|
|
|
|
result.storage = malloc(result.maxp.max_storage * sizeof(struct ttf_function));
|
|
|
|
}
|
|
|
|
|
|
|
|
//struct ttf_graphics_state fpgm_state = default_graphics_state();
|
|
|
|
//struct ttf_graphics_state prep_state = default_graphics_state();
|
|
|
|
|
|
|
|
//read_fpgm(&result, &font_file, font_dir, &fpgm_state); // the font program
|
|
|
|
//printf("------------- FPGM END -------------\n");
|
|
|
|
//read_cvt(&result, &font_file, font_dir, &prep_state); // the cvt program
|
|
|
|
//printf("------------- CVT END -------------\n");
|
|
|
|
|
|
|
|
result.name = fontname;
|
|
|
|
|
|
|
|
return(result);
|
|
|
|
}
|