/* epd_font.c */ /* Includes */ #include "epd.h" #include "epd_font.h" #include "epd_fonts.h" #include "util.h" /* Definitions */ #define EPD_FONT_DATA_STRUCT_SIZE 23 #define epd_pgm_read(adr) (*(const uint8_t *) (adr)) /* Function Prototypes */ static void epd_read_fontcfg(epd_fontcfg_t *cfg, epd_font_t *font); static uint8_t epd_font_get_byte(epd_font_t *font, uint8_t offset); static uint16_t epd_font_get_word(epd_font_t *font, uint8_t offset); static void epd_update_ref_height(epd_t *epd); static uint16_t epd_ascii_next(UNUSED epd_t *epd, uint8_t b); static uint16_t epd_utf8_next(epd_t *epd, uint8_t b); static uint8_t epd_draw_string(epd_t *epd, uint32_t x, uint32_t y, const char *str); static uint8_t epd_draw_glyph(epd_t *epd, uint32_t x, uint32_t y, uint16_t encoding); static uint8_t epd_font_draw_glyph(epd_t *epd, uint32_t x, uint32_t y, uint16_t encoding); epd_font_t *epd_font_get_glyph_data(epd_t *epd, uint16_t encoding); static int8_t epd_font_decode_glyph(epd_t *epd, const uint8_t *glyph_data); static void epd_font_setup_decode(epd_t *epd, const uint8_t *glyph_data); static int8_t epd_font_decode_get_signed_bits(epd_font_decode_t *f, uint8_t cnt); static uint8_t epd_font_decode_get_unsigned_bits(epd_font_decode_t *f, uint8_t cnt); static void epd_font_decode_len(epd_t *epd, uint8_t len, uint8_t is_foreground); /* Function Definitions */ void epd_font_set(epd_t *epd, epd_font_t *font) { if(epd->font != font) { epd->font = font; epd_read_fontcfg(&(epd->fontdata.cfg), font); epd_update_ref_height(epd); } } static void epd_read_fontcfg(epd_fontcfg_t *cfg, epd_font_t *font) { /* offset 0 */ cfg->glyph_cnt = epd_font_get_byte(font, 0); cfg->bbx_mode = epd_font_get_byte(font, 1); cfg->bits_per_0 = epd_font_get_byte(font, 2); cfg->bits_per_1 = epd_font_get_byte(font, 3); /* offset 4 */ cfg->bits_per_char_width = epd_font_get_byte(font, 4); cfg->bits_per_char_height = epd_font_get_byte(font, 5); cfg->bits_per_char_x = epd_font_get_byte(font, 6); cfg->bits_per_char_y = epd_font_get_byte(font, 7); cfg->bits_per_delta_x = epd_font_get_byte(font, 8); /* offset 9 */ cfg->max_char_width = epd_font_get_byte(font, 9); cfg->max_char_height = epd_font_get_byte(font, 10); cfg->x_offset = epd_font_get_byte(font, 11); cfg->y_offset = epd_font_get_byte(font, 12); /* offset 13 */ cfg->ascent_A = epd_font_get_byte(font, 13); cfg->descent_g = epd_font_get_byte(font, 14); cfg->ascent_para = epd_font_get_byte(font, 15); cfg->descent_para = epd_font_get_byte(font, 16); /* offset 17 */ cfg->start_pos_upper_A = epd_font_get_word(font, 17); cfg->start_pos_lower_a = epd_font_get_word(font, 19); #ifdef EPD_WITH_UNICODE /* offset 21 */ fontcfg->start_pos_unicode = epd_font_get_word(font, 21); #endif } static uint8_t epd_font_get_byte(epd_font_t *font, uint8_t offset) { font += offset; return epd_pgm_read(font); } static uint16_t epd_font_get_word(epd_font_t *font, uint8_t offset) { uint16_t pos; font += offset; pos = epd_pgm_read(font); font++; pos <<= 8; pos += epd_pgm_read(font); return pos; } static void epd_update_ref_height(epd_t *epd) { return_if_fail(epd->font); epd->fontdata.ref_ascent = epd->fontdata.cfg.ascent_A; epd->fontdata.ref_descent = epd->fontdata.cfg.descent_g; if(epd->fontdata.height_mode == EPD_FONT_HEIGHT_MODE_TEXT) { } else if(epd->fontdata.height_mode == EPD_FONT_HEIGHT_MODE_XTEXT) { if(epd->fontdata.ref_ascent < epd->fontdata.cfg.ascent_para) { epd->fontdata.ref_ascent = epd->fontdata.cfg.ascent_para; } if(epd->fontdata.ref_descent > epd->fontdata.cfg.descent_para) { epd->fontdata.ref_descent = epd->fontdata.cfg.descent_para; } } else { if(epd->fontdata.ref_ascent < epd->fontdata.cfg.max_char_height + epd->fontdata.cfg.y_offset) { epd->fontdata.ref_ascent = epd->fontdata.cfg.max_char_height + epd->fontdata.cfg.y_offset; } if(epd->fontdata.ref_descent > epd->fontdata.cfg.y_offset) { epd->fontdata.ref_descent = epd->fontdata.cfg.y_offset; } } } uint8_t epd_draw_str(epd_t *epd, uint32_t x, uint32_t y, const char *str) { epd->fontdata.char_cb = epd_ascii_next; return epd_draw_string(epd, x, y, str); } uint8_t epd_draw_utf8(epd_t *epd, uint32_t x, uint32_t y, const char *str) { epd->fontdata.char_cb = epd_utf8_next; return epd_draw_string(epd, x, y, str); } static uint16_t epd_ascii_next(UNUSED epd_t *epd, uint8_t b) { if(b == 0 || b == '\n') /* '\n' terminates the string to support the string list procedures */ { return 0xffff; /* end of string detected*/ } return b; } /* pass a byte from an utf8 encoded string to the utf8 decoder state machine returns 0xfffe: no glyph, just continue 0xffff: end of string anything else: The decoded encoding */ static uint16_t epd_utf8_next(epd_t *epd, uint8_t b) { if(b == 0 || b == '\n') /* '\n' terminates the string to support the string list procedures */ { return 0xffff; /* end of string detected, pending UTF8 is discarded */ } if(epd->fontdata.utf8_state == 0) { if(b >= 0xfc) /* 6 byte sequence */ { epd->fontdata.utf8_state = 5; b &= 0x01; } else if(b >= 0xf8) { epd->fontdata.utf8_state = 4; b &= 0x03; } else if(b >= 0xf0) { epd->fontdata.utf8_state = 3; b &= 0x07; } else if(b >= 0xe0) { epd->fontdata.utf8_state = 2; b &= 0x0f; } else if(b >= 0xc0) { epd->fontdata.utf8_state = 1; b &= 0x1f; } else { /* do nothing, just use the value as encoding */ return b; } epd->fontdata.encoding = b; return 0xfffe; } else { epd->fontdata.utf8_state--; /* The case b < 0x080 (an illegal UTF8 encoding) is not checked here. */ epd->fontdata.encoding <<= 6; b &= 0x3f; epd->fontdata.encoding |= b; if(epd->fontdata.utf8_state != 0) { return 0xfffe; /* nothing to do yet */ } } return epd->fontdata.encoding; } /* reset the internal state machine */ void epd_utf8_init(epd_t *epd) { epd->fontdata.utf8_state = 0; } static uint8_t epd_draw_string(epd_t *epd, uint32_t x, uint32_t y, const char *str) { uint16_t e; uint8_t delta, sum; epd_utf8_init(epd); sum = 0; for(;;) { e = epd->fontdata.char_cb(epd, (uint8_t) *str); if(e == 0xffff) break; str++; if(e != 0xfffe) { delta = epd_draw_glyph(epd, x, y, e); #ifdef EPD_WITH_FONT_ROTATION switch(epd->fontdata.decode.dir) { case 0: x += delta; break; case 1: y += delta; break; case 2: x -= delta; break; case 3: y -= delta; break; } /* // requires 10 bytes more on avr x = epd_add_vector_x(x, delta, 0, epd->fontdata.decode.dir); y = epd_add_vector_y(y, delta, 0, epd->fontdata.decode.dir); */ #else x += delta; #endif sum += delta; } } return sum; } static uint8_t epd_draw_glyph(epd_t *epd, uint32_t x, uint32_t y, uint16_t encoding) { #ifdef EPD_WITH_FONT_ROTATION switch(epd->fontdata.decode.dir) { case 0: y += epd->font_calc_vref(epd); break; case 1: x -= epd->font_calc_vref(epd); break; case 2: y -= epd->font_calc_vref(epd); break; case 3: x += epd->font_calc_vref(epd); break; } #else /* FIXME */ /* y += epd->font_calc_vref(epd); */ #endif return epd_font_draw_glyph(epd, x, y, encoding); } static uint8_t epd_font_draw_glyph(epd_t *epd, uint32_t x, uint32_t y, uint16_t encoding) { uint8_t dx = 0; epd->fontdata.decode.target_x = x; epd->fontdata.decode.target_y = y; //epd->fontdata.decode.is_transparent = is_transparent; this is already set //epd->fontdata.decode.dir = dir; const uint8_t *glyph_data = epd_font_get_glyph_data(epd, encoding); if(glyph_data != NULL) { dx = epd_font_decode_glyph(epd, glyph_data); } return dx; } /* Description: Find the starting point of the glyph data. Args: encoding: Encoding (ASCII or Unicode) of the glyph Return: Address of the glyph data or NULL, if the encoding is not avialable in the font. */ epd_font_t *epd_font_get_glyph_data(epd_t *epd, uint16_t encoding) { epd_font_t *font = epd->font; font += EPD_FONT_DATA_STRUCT_SIZE; if(encoding <= 255) { if(encoding >= 'a') { font += epd->fontdata.cfg.start_pos_lower_a; } else if(encoding >= 'A') { font += epd->fontdata.cfg.start_pos_upper_A; } for(;;) { if(epd_pgm_read(font + 1) == 0) { break; } if(epd_pgm_read(font) == encoding) { return font + 2; /* skip encoding and glyph size */ } font += epd_pgm_read(font + 1); } } #ifdef EPD_WITH_UNICODE else { uint16_t e; const uint8_t *unicode_lookup_table; font += epd->fontdata.cfg.start_pos_unicode; unicode_lookup_table = font; /* issue 596: search for the glyph start in the unicode lookup table */ do { font += epd_font_get_word(unicode_lookup_table, 0); e = epd_font_get_word(unicode_lookup_table, 2); unicode_lookup_table+=4; } while(e < encoding); for(;;) { e = epd_pgm_read(font); e <<= 8; e |= epd_pgm_read(font + 1); if(e == 0) break; if(e == encoding) { return font+3; /* skip encoding and glyph size */ } font += epd_pgm_read(font + 2); } } #endif return NULL; } /* Description: Decode and draw a glyph. Args: glyph_data: Pointer to the compressed glyph data of the font epd->fontdata.decode.target_x X position epd->fontdata.decode.target_y Y position epd->fontdata.decode.is_transparent Transparent mode Return: Width (delta x advance) of the glyph. Calls: epd_font_decode_len() */ /* optimized */ int8_t epd_font_decode_glyph(epd_t *epd, const uint8_t *glyph_data) { uint8_t a, b; int8_t x, y; int8_t d; int8_t h; epd_font_decode_t *decode = &(epd->fontdata.decode); epd_font_setup_decode(epd, glyph_data); /* set values in epd->fontdata.decode data structure */ h = epd->fontdata.decode.glyph_height; x = epd_font_decode_get_signed_bits(decode, epd->fontdata.cfg.bits_per_char_x); y = epd_font_decode_get_signed_bits(decode, epd->fontdata.cfg.bits_per_char_y); d = epd_font_decode_get_signed_bits(decode, epd->fontdata.cfg.bits_per_delta_x); if(decode->glyph_width > 0) { #ifdef EPD_WITH_FONT_ROTATION decode->target_x = epd_add_vector_x(decode->target_x, x, -(h+y), decode->dir); decode->target_y = epd_add_vector_y(decode->target_y, x, -(h+y), decode->dir); //epd_add_vector(&(decode->target_x), &(decode->target_y), x, -(h+y), decode->dir); #else decode->target_x += x; decode->target_y -= h+y; #endif //epd_add_vector(&(decode->target_x), &(decode->target_y), x, -(h+y), decode->dir); #ifdef EPD_WITH_INTERSECTION { uint32_t x0, x1, y0, y1; x0 = decode->target_x; y0 = decode->target_y; x1 = x0; y1 = y0; #ifdef EPD_WITH_FONT_ROTATION switch(decode->dir) { case 0: x1 += decode->glyph_width; y1 += h; break; case 1: x0 -= h; x0++; /* shift down, because of assymetric boundaries for the interseciton test */ x1++; y1 += decode->glyph_width; break; case 2: x0 -= decode->glyph_width; x0++; /* shift down, because of assymetric boundaries for the interseciton test */ x1++; y0 -= h; y0++; /* shift down, because of assymetric boundaries for the interseciton test */ y1++; break; case 3: x1 += h; y0 -= decode->glyph_width; y0++; /* shift down, because of assymetric boundaries for the interseciton test */ y1++; break; } #else /* EPD_WITH_FONT_ROTATION */ x1 += decode->glyph_width; y1 += h; #endif if(epd_IsIntersection(epd, x0, y0, x1, y1) == 0) return d; } #endif /* EPD_WITH_INTERSECTION */ /* reset local x/y position */ decode->x = 0; decode->y = 0; /* decode glyph */ for(;;) { a = epd_font_decode_get_unsigned_bits(decode, epd->fontdata.cfg.bits_per_0); b = epd_font_decode_get_unsigned_bits(decode, epd->fontdata.cfg.bits_per_1); do { epd_font_decode_len(epd, a, 0); epd_font_decode_len(epd, b, 1); } while(epd_font_decode_get_unsigned_bits(decode, 1) != 0); if(decode->y >= h) { break; } } /* restore the epd draw color, because this is modified by the decode algo */ epd->draw_color = decode->fg_color; } return d; } static void epd_font_setup_decode(epd_t *epd, const uint8_t *glyph_data) { epd_font_decode_t *decode = &(epd->fontdata.decode); decode->decode_ptr = glyph_data; decode->decode_bit_pos = 0; /* 8 Nov 2015, this is already done in the glyph data search procedure */ /* decode->decode_ptr += 1; decode->decode_ptr += 1; */ decode->glyph_width = epd_font_decode_get_unsigned_bits(decode, epd->fontdata.cfg.bits_per_char_width); decode->glyph_height = epd_font_decode_get_unsigned_bits(decode, epd->fontdata.cfg.bits_per_char_height); decode->fg_color = epd->draw_color; decode->bg_color = (decode->fg_color == 0 ? 1 : 0); } /* 2 bit --> cnt = 2 -2,-1,0. 1 3 bit --> cnt = 3 -2,-1,0. 1 -4,-3,-2,-1,0,1,2,3 if(x < 0) r = bits(x-1)+1; else r = bits(x)+1; */ /* optimized */ static int8_t epd_font_decode_get_signed_bits(epd_font_decode_t *f, uint8_t cnt) { int8_t v, d; v = (int8_t)epd_font_decode_get_unsigned_bits(f, cnt); d = 1; cnt--; d <<= cnt; v -= d; return v; //return (int8_t)epd_font_decode_get_unsigned_bits(f, cnt) - ((1<>1); } /* optimized */ static uint8_t epd_font_decode_get_unsigned_bits(epd_font_decode_t *f, uint8_t cnt) { uint8_t val; uint8_t bit_pos = f->decode_bit_pos; uint8_t bit_pos_plus_cnt; val = epd_pgm_read(f->decode_ptr); val >>= bit_pos; bit_pos_plus_cnt = bit_pos; bit_pos_plus_cnt += cnt; if(bit_pos_plus_cnt >= 8) { f->decode_ptr++; val |= epd_pgm_read(f->decode_ptr) << (8 - bit_pos); bit_pos_plus_cnt -= 8; } val &= (1U << cnt) - 1; f->decode_bit_pos = bit_pos_plus_cnt; return val; } /* Description: Draw a run-length area of the glyph. "len" can have any size and the line length has to be wrapped at the glyph border. Args: len: Length of the line is_foreground foreground/background? epd->fontdata.decode.target_x X position epd->fontdata.decode.target_y Y position epd->fontdata.decode.is_transparent Transparent mode Return: - Calls: epd_Draw90Line() Called by: epd_font_decode_glyph() */ /* optimized */ static void epd_font_decode_len(epd_t *epd, uint8_t len, uint8_t is_foreground) { uint8_t cnt; /* total number of remaining pixels, which have to be drawn */ uint8_t rem; /* remaining pixel to the right edge of the glyph */ uint8_t current; /* number of pixels, which need to be drawn for the draw procedure */ /* current is either equal to cnt or equal to rem */ /* local coordinates of the glyph */ uint8_t lx,ly; /* target position on the screen */ uint16_t x, y; epd_font_decode_t *decode = &(epd->fontdata.decode); cnt = len; /* get the local position */ lx = decode->x; ly = decode->y; for(;;) { /* calculate the number of pixel to the right edge of the glyph */ rem = decode->glyph_width; rem -= lx; /* calculate how many pixel to draw. This is either to the right edge */ /* or lesser, if not enough pixel are left */ current = rem; if(cnt < rem) current = cnt; /* now draw the line, but apply the rotation around the glyph target position */ //epd_font_decode_draw_pixel(epd, lx,ly,current, is_foreground); /* get target position */ x = decode->target_x; y = decode->target_y; /* apply rotation */ #ifdef EPD_WITH_FONT_ROTATION x = epd_add_vector_x(x, lx, ly, decode->dir); y = epd_add_vector_y(y, lx, ly, decode->dir); //epd_add_vector(&x, &y, lx, ly, decode->dir); #else x += lx; y += ly; #endif /* draw foreground and background (if required) */ if(is_foreground) { epd->draw_color = decode->fg_color; /* draw_color will be restored later */ epd_draw_line(epd, x, y, current, #ifdef EPD_WITH_FONT_ROTATION decode->dir #else EPD_DIR_L2R #endif ); } else if(decode->is_transparent == 0) { epd->draw_color = decode->bg_color; /* draw_color will be restored later */ epd_draw_line(epd, x, y, current, #ifdef EPD_WITH_FONT_ROTATION decode->dir #else EPD_DIR_L2R #endif ); } /* check, whether the end of the run length code has been reached */ if(cnt < rem) { break; } cnt -= rem; lx = 0; ly++; } lx += cnt; decode->x = lx; decode->y = ly; }