/* u8g2_font.c Universal 8bit Graphics Library (https://github.com/olikraus/u8g2/) Copyright (c) 2016, olikraus@gmail.com All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "u8g2.h" /* size of the font data structure, there is no struct or class... */ /* this is the size for the new font format */ #define U8G2_FONT_DATA_STRUCT_SIZE 23 /* font data: offset bytes description 0 1 glyph_cnt number of glyphs 1 1 bbx_mode 0: proportional, 1: common height, 2: monospace, 3: multiple of 8 2 1 bits_per_0 glyph rle parameter 3 1 bits_per_1 glyph rle parameter 4 1 bits_per_char_width glyph rle parameter 5 1 bits_per_char_height glyph rle parameter 6 1 bits_per_char_x glyph rle parameter 7 1 bits_per_char_y glyph rle parameter 8 1 bits_per_delta_x glyph rle parameter 9 1 max_char_width 10 1 max_char_height 11 1 x offset 12 1 y offset (descent) 13 1 ascent (capital A) 14 1 descent (lower g) 15 1 ascent '(' 16 1 descent ')' 17 1 start pos 'A' high byte 18 1 start pos 'A' low byte 19 1 start pos 'a' high byte 20 1 start pos 'a' low byte 21 1 start pos unicode high byte 22 1 start pos unicode low byte Font build mode, 0: proportional, 1: common height, 2: monospace, 3: multiple of 8 Font build mode 0: - "t" - Ref height mode: U8G2_FONT_HEIGHT_MODE_TEXT, U8G2_FONT_HEIGHT_MODE_XTEXT or U8G2_FONT_HEIGHT_MODE_ALL - use in transparent mode only (does not look good in solid mode) - most compact format - different font heights possible Font build mode 1: - "h" - Ref height mode: U8G2_FONT_HEIGHT_MODE_ALL - transparent or solid mode - The height of the glyphs depend on the largest glyph in the font. This means font height depends on postfix "r", "f" and "n". */ /* use case: What is the width and the height of the minimal box into which string s fints? */ void u8g2_font_GetStrSize(const void* font, const char* s, u8g2_uint_t* width, u8g2_uint_t* height); void u8g2_font_GetStrSizeP( const void* font, const char* s, u8g2_uint_t* width, u8g2_uint_t* height); /* use case: lower left edge of a minimal box is known, what is the correct x, y position for the string draw procedure */ void u8g2_font_AdjustXYToDraw(const void* font, const char* s, u8g2_uint_t* x, u8g2_uint_t* y); void u8g2_font_AdjustXYToDrawP(const void* font, const char* s, u8g2_uint_t* x, u8g2_uint_t* y); /* use case: Baseline origin known, return minimal box */ void u8g2_font_GetStrMinBox( u8g2_t* u8g2, const void* font, const char* s, u8g2_uint_t* x, u8g2_uint_t* y, u8g2_uint_t* width, u8g2_uint_t* height); /* procedures */ /*========================================================================*/ /* low level byte and word access */ /* removed NOINLINE, because it leads to smaller code, might also be faster */ //static uint8_t u8g2_font_get_byte(const uint8_t *font, uint8_t offset) U8G2_NOINLINE; static uint8_t u8g2_font_get_byte(const uint8_t* font, uint8_t offset) { font += offset; return u8x8_pgm_read(font); } static uint16_t u8g2_font_get_word(const uint8_t* font, uint8_t offset) U8G2_NOINLINE; static uint16_t u8g2_font_get_word(const uint8_t* font, uint8_t offset) { uint16_t pos; font += offset; pos = u8x8_pgm_read(font); font++; pos <<= 8; pos += u8x8_pgm_read(font); return pos; } /*========================================================================*/ /* new font format */ void u8g2_read_font_info(u8g2_font_info_t* font_info, const uint8_t* font) { /* offset 0 */ font_info->glyph_cnt = u8g2_font_get_byte(font, 0); font_info->bbx_mode = u8g2_font_get_byte(font, 1); font_info->bits_per_0 = u8g2_font_get_byte(font, 2); font_info->bits_per_1 = u8g2_font_get_byte(font, 3); /* offset 4 */ font_info->bits_per_char_width = u8g2_font_get_byte(font, 4); font_info->bits_per_char_height = u8g2_font_get_byte(font, 5); font_info->bits_per_char_x = u8g2_font_get_byte(font, 6); font_info->bits_per_char_y = u8g2_font_get_byte(font, 7); font_info->bits_per_delta_x = u8g2_font_get_byte(font, 8); /* offset 9 */ font_info->max_char_width = u8g2_font_get_byte(font, 9); font_info->max_char_height = u8g2_font_get_byte(font, 10); font_info->x_offset = u8g2_font_get_byte(font, 11); font_info->y_offset = u8g2_font_get_byte(font, 12); /* offset 13 */ font_info->ascent_A = u8g2_font_get_byte(font, 13); font_info->descent_g = u8g2_font_get_byte(font, 14); font_info->ascent_para = u8g2_font_get_byte(font, 15); font_info->descent_para = u8g2_font_get_byte(font, 16); /* offset 17 */ font_info->start_pos_upper_A = u8g2_font_get_word(font, 17); font_info->start_pos_lower_a = u8g2_font_get_word(font, 19); /* offset 21 */ #ifdef U8G2_WITH_UNICODE font_info->start_pos_unicode = u8g2_font_get_word(font, 21); #endif } /* calculate the overall length of the font, only used to create the picture for the google wiki */ size_t u8g2_GetFontSize(const uint8_t* font_arg) { uint16_t e; const uint8_t* font = font_arg; font += U8G2_FONT_DATA_STRUCT_SIZE; for(;;) { if(u8x8_pgm_read(font + 1) == 0) break; font += u8x8_pgm_read(font + 1); } /* continue with unicode section */ font += 2; /* skip unicode lookup table */ font += u8g2_font_get_word(font, 0); for(;;) { e = u8x8_pgm_read(font); e <<= 8; e |= u8x8_pgm_read(font + 1); if(e == 0) break; font += u8x8_pgm_read(font + 2); } return (font - font_arg) + 2; } /*========================================================================*/ /* u8g2 interface, font access */ uint8_t u8g2_GetFontBBXWidth(u8g2_t* u8g2) { return u8g2->font_info.max_char_width; /* new font info structure */ } uint8_t u8g2_GetFontBBXHeight(u8g2_t* u8g2) { return u8g2->font_info.max_char_height; /* new font info structure */ } int8_t u8g2_GetFontBBXOffX(u8g2_t* u8g2) U8G2_NOINLINE; int8_t u8g2_GetFontBBXOffX(u8g2_t* u8g2) { return u8g2->font_info.x_offset; /* new font info structure */ } int8_t u8g2_GetFontBBXOffY(u8g2_t* u8g2) U8G2_NOINLINE; int8_t u8g2_GetFontBBXOffY(u8g2_t* u8g2) { return u8g2->font_info.y_offset; /* new font info structure */ } uint8_t u8g2_GetFontCapitalAHeight(u8g2_t* u8g2) U8G2_NOINLINE; uint8_t u8g2_GetFontCapitalAHeight(u8g2_t* u8g2) { return u8g2->font_info.ascent_A; /* new font info structure */ } /*========================================================================*/ /* glyph handling */ /* optimized */ uint8_t u8g2_font_decode_get_unsigned_bits(u8g2_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 = *(f->decode_ptr); val = u8x8_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) { uint8_t s = 8; s -= bit_pos; f->decode_ptr++; //val |= *(f->decode_ptr) << (8-bit_pos); val |= u8x8_pgm_read(f->decode_ptr) << (s); //bit_pos -= 8; bit_pos_plus_cnt -= 8; } val &= (1U << cnt) - 1; //bit_pos += cnt; f->decode_bit_pos = bit_pos_plus_cnt; return val; } /* 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 */ int8_t u8g2_font_decode_get_signed_bits(u8g2_font_decode_t* f, uint8_t cnt) { int8_t v, d; v = (int8_t)u8g2_font_decode_get_unsigned_bits(f, cnt); d = 1; cnt--; d <<= cnt; v -= d; return v; //return (int8_t)u8g2_font_decode_get_unsigned_bits(f, cnt) - ((1<>1); } #ifdef U8G2_WITH_FONT_ROTATION u8g2_uint_t u8g2_add_vector_y(u8g2_uint_t dy, int8_t x, int8_t y, uint8_t dir) { switch(dir) { case 0: dy += y; break; case 1: dy += x; break; case 2: dy -= y; break; default: dy -= x; break; } return dy; } u8g2_uint_t u8g2_add_vector_x(u8g2_uint_t dx, int8_t x, int8_t y, uint8_t dir) { switch(dir) { case 0: dx += x; break; case 1: dx -= y; break; case 2: dx -= x; break; default: dx += y; break; } return dx; } /* // does not make sense, 50 bytes more required on avr void u8g2_add_vector(u8g2_uint_t *xp, u8g2_uint_t *yp, int8_t x, int8_t y, uint8_t dir) { u8g2_uint_t x_ = *xp; u8g2_uint_t y_ = *yp; switch(dir) { case 0: y_ += y; x_ += x; break; case 1: y_ += x; x_ -= y; break; case 2: y_ -= y; x_ -= x; break; default: y_ -= x; x_ += y; break; } *xp = x_; *yp = y_; } */ #endif /* 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? u8g2->font_decode.target_x X position u8g2->font_decode.target_y Y position u8g2->font_decode.is_transparent Transparent mode Return: - Calls: u8g2_Draw90Line() Called by: u8g2_font_decode_glyph() */ /* optimized */ void u8g2_font_decode_len(u8g2_t* u8g2, 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 */ u8g2_uint_t x, y; u8g2_font_decode_t* decode = &(u8g2->font_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 */ //u8g2_font_decode_draw_pixel(u8g2, lx,ly,current, is_foreground); /* get target position */ x = decode->target_x; y = decode->target_y; /* apply rotation */ #ifdef U8G2_WITH_FONT_ROTATION x = u8g2_add_vector_x(x, lx, ly, decode->dir); y = u8g2_add_vector_y(y, lx, ly, decode->dir); //u8g2_add_vector(&x, &y, lx, ly, decode->dir); #else x += lx; y += ly; #endif /* draw foreground and background (if required) */ if(is_foreground) { u8g2->draw_color = decode->fg_color; /* draw_color will be restored later */ u8g2_DrawHVLine( u8g2, x, y, current, #ifdef U8G2_WITH_FONT_ROTATION /* dir */ decode->dir #else 0 #endif ); } else if(decode->is_transparent == 0) { u8g2->draw_color = decode->bg_color; /* draw_color will be restored later */ u8g2_DrawHVLine( u8g2, x, y, current, #ifdef U8G2_WITH_FONT_ROTATION /* dir */ decode->dir #else 0 #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; } static void u8g2_font_setup_decode(u8g2_t* u8g2, const uint8_t* glyph_data) { u8g2_font_decode_t* decode = &(u8g2->font_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 = u8g2_font_decode_get_unsigned_bits(decode, u8g2->font_info.bits_per_char_width); decode->glyph_height = u8g2_font_decode_get_unsigned_bits(decode, u8g2->font_info.bits_per_char_height); decode->fg_color = u8g2->draw_color; decode->bg_color = (decode->fg_color == 0 ? 1 : 0); } /* Description: Decode and draw a glyph. Args: glyph_data: Pointer to the compressed glyph data of the font u8g2->font_decode.target_x X position u8g2->font_decode.target_y Y position u8g2->font_decode.is_transparent Transparent mode Return: Width (delta x advance) of the glyph. Calls: u8g2_font_decode_len() */ /* optimized */ int8_t u8g2_font_decode_glyph(u8g2_t* u8g2, const uint8_t* glyph_data) { uint8_t a, b; int8_t x, y; int8_t d; int8_t h; u8g2_font_decode_t* decode = &(u8g2->font_decode); u8g2_font_setup_decode(u8g2, glyph_data); h = u8g2->font_decode.glyph_height; x = u8g2_font_decode_get_signed_bits(decode, u8g2->font_info.bits_per_char_x); y = u8g2_font_decode_get_signed_bits(decode, u8g2->font_info.bits_per_char_y); d = u8g2_font_decode_get_signed_bits(decode, u8g2->font_info.bits_per_delta_x); if(decode->glyph_width > 0) { #ifdef U8G2_WITH_FONT_ROTATION decode->target_x = u8g2_add_vector_x(decode->target_x, x, -(h + y), decode->dir); decode->target_y = u8g2_add_vector_y(decode->target_y, x, -(h + y), decode->dir); //u8g2_add_vector(&(decode->target_x), &(decode->target_y), x, -(h+y), decode->dir); #else decode->target_x += x; decode->target_y -= h + y; #endif //u8g2_add_vector(&(decode->target_x), &(decode->target_y), x, -(h+y), decode->dir); #ifdef U8G2_WITH_INTERSECTION { u8g2_uint_t x0, x1, y0, y1; x0 = decode->target_x; y0 = decode->target_y; x1 = x0; y1 = y0; #ifdef U8G2_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 /* U8G2_WITH_FONT_ROTATION */ x1 += decode->glyph_width; y1 += h; #endif if(u8g2_IsIntersection(u8g2, x0, y0, x1, y1) == 0) return d; } #endif /* U8G2_WITH_INTERSECTION */ /* reset local x/y position */ decode->x = 0; decode->y = 0; /* decode glyph */ for(;;) { a = u8g2_font_decode_get_unsigned_bits(decode, u8g2->font_info.bits_per_0); b = u8g2_font_decode_get_unsigned_bits(decode, u8g2->font_info.bits_per_1); do { u8g2_font_decode_len(u8g2, a, 0); u8g2_font_decode_len(u8g2, b, 1); } while(u8g2_font_decode_get_unsigned_bits(decode, 1) != 0); if(decode->y >= h) break; } /* restore the u8g2 draw color, because this is modified by the decode algo */ u8g2->draw_color = decode->fg_color; } return d; } /* 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. */ const uint8_t* u8g2_font_get_glyph_data(u8g2_t* u8g2, uint16_t encoding) { const uint8_t* font = u8g2->font; font += U8G2_FONT_DATA_STRUCT_SIZE; if(encoding <= 255) { if(encoding >= 'a') { font += u8g2->font_info.start_pos_lower_a; } else if(encoding >= 'A') { font += u8g2->font_info.start_pos_upper_A; } for(;;) { if(u8x8_pgm_read(font + 1) == 0) break; if(u8x8_pgm_read(font) == encoding) { return font + 2; /* skip encoding and glyph size */ } font += u8x8_pgm_read(font + 1); } } #ifdef U8G2_WITH_UNICODE else { uint16_t e; const uint8_t* unicode_lookup_table; // removed, there is now the new index table //#ifdef __unix__ // if ( u8g2->last_font_data != NULL && encoding >= u8g2->last_unicode ) // { // font = u8g2->last_font_data; // } // else //#endif font += u8g2->font_info.start_pos_unicode; unicode_lookup_table = font; /* issue 596: search for the glyph start in the unicode lookup table */ do { font += u8g2_font_get_word(unicode_lookup_table, 0); e = u8g2_font_get_word(unicode_lookup_table, 2); unicode_lookup_table += 4; } while(e < encoding); for(;;) { e = u8x8_pgm_read(font); e <<= 8; e |= u8x8_pgm_read(font + 1); // removed, there is now the new index table //#ifdef __unix__ // if ( encoding < e ) // break; //#endif if(e == 0) break; if(e == encoding) { // removed, there is now the new index table //#ifdef __unix__ // u8g2->last_font_data = font; // u8g2->last_unicode = encoding; //#endif return font + 3; /* skip encoding and glyph size */ } font += u8x8_pgm_read(font + 2); } } #endif return NULL; } static u8g2_uint_t u8g2_font_draw_glyph(u8g2_t* u8g2, u8g2_uint_t x, u8g2_uint_t y, uint16_t encoding) { u8g2_uint_t dx = 0; u8g2->font_decode.target_x = x; u8g2->font_decode.target_y = y; //u8g2->font_decode.is_transparent = is_transparent; this is already set //u8g2->font_decode.dir = dir; const uint8_t* glyph_data = u8g2_font_get_glyph_data(u8g2, encoding); if(glyph_data != NULL) { dx = u8g2_font_decode_glyph(u8g2, glyph_data); } return dx; } uint8_t u8g2_IsGlyph(u8g2_t* u8g2, uint16_t requested_encoding) { /* updated to new code */ if(u8g2_font_get_glyph_data(u8g2, requested_encoding) != NULL) return 1; return 0; } /* side effect: updates u8g2->font_decode and u8g2->glyph_x_offset */ int8_t u8g2_GetGlyphWidth(u8g2_t* u8g2, uint16_t requested_encoding) { const uint8_t* glyph_data = u8g2_font_get_glyph_data(u8g2, requested_encoding); if(glyph_data == NULL) return 0; u8g2_font_setup_decode(u8g2, glyph_data); u8g2->glyph_x_offset = u8g2_font_decode_get_signed_bits(&(u8g2->font_decode), u8g2->font_info.bits_per_char_x); u8g2_font_decode_get_signed_bits(&(u8g2->font_decode), u8g2->font_info.bits_per_char_y); /* glyph width is here: u8g2->font_decode.glyph_width */ return u8g2_font_decode_get_signed_bits( &(u8g2->font_decode), u8g2->font_info.bits_per_delta_x); } /* set one of: U8G2_FONT_MODE_TRANSPARENT U8G2_FONT_MODE_SOLID U8G2_FONT_MODE_NONE This has been changed for the new font procedures */ void u8g2_SetFontMode(u8g2_t* u8g2, uint8_t is_transparent) { u8g2->font_decode.is_transparent = is_transparent; // new font procedures } u8g2_uint_t u8g2_DrawGlyph(u8g2_t* u8g2, u8g2_uint_t x, u8g2_uint_t y, uint16_t encoding) { #ifdef U8G2_WITH_FONT_ROTATION switch(u8g2->font_decode.dir) { case 0: y += u8g2->font_calc_vref(u8g2); break; case 1: x -= u8g2->font_calc_vref(u8g2); break; case 2: y -= u8g2->font_calc_vref(u8g2); break; case 3: x += u8g2->font_calc_vref(u8g2); break; } #else y += u8g2->font_calc_vref(u8g2); #endif return u8g2_font_draw_glyph(u8g2, x, y, encoding); } static u8g2_uint_t u8g2_draw_string(u8g2_t* u8g2, u8g2_uint_t x, u8g2_uint_t y, const char* str) U8G2_NOINLINE; static u8g2_uint_t u8g2_draw_string(u8g2_t* u8g2, u8g2_uint_t x, u8g2_uint_t y, const char* str) { uint16_t e; u8g2_uint_t delta, sum; u8x8_utf8_init(u8g2_GetU8x8(u8g2)); sum = 0; for(;;) { e = u8g2->u8x8.next_cb(u8g2_GetU8x8(u8g2), (uint8_t)*str); if(e == 0x0ffff) break; str++; if(e != 0x0fffe) { delta = u8g2_DrawGlyph(u8g2, x, y, e); #ifdef U8G2_WITH_FONT_ROTATION switch(u8g2->font_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 = u8g2_add_vector_x(x, delta, 0, u8g2->font_decode.dir); y = u8g2_add_vector_y(y, delta, 0, u8g2->font_decode.dir); */ #else x += delta; #endif sum += delta; } } return sum; } u8g2_uint_t u8g2_DrawStr(u8g2_t* u8g2, u8g2_uint_t x, u8g2_uint_t y, const char* str) { u8g2->u8x8.next_cb = u8x8_ascii_next; return u8g2_draw_string(u8g2, x, y, str); } /* source: https://en.wikipedia.org/wiki/UTF-8 Bits from to bytes Byte 1 Byte 2 Byte 3 Byte 4 Byte 5 Byte 6 7 U+0000 U+007F 1 0xxxxxxx 11 U+0080 U+07FF 2 110xxxxx 10xxxxxx 16 U+0800 U+FFFF 3 1110xxxx 10xxxxxx 10xxxxxx 21 U+10000 U+1FFFFF 4 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 26 U+200000 U+3FFFFFF 5 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 31 U+4000000 U+7FFFFFFF 6 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx */ u8g2_uint_t u8g2_DrawUTF8(u8g2_t* u8g2, u8g2_uint_t x, u8g2_uint_t y, const char* str) { u8g2->u8x8.next_cb = u8x8_utf8_next; return u8g2_draw_string(u8g2, x, y, str); } u8g2_uint_t u8g2_DrawExtendedUTF8( u8g2_t* u8g2, u8g2_uint_t x, u8g2_uint_t y, uint8_t to_left, u8g2_kerning_t* kerning, const char* str) { u8g2->u8x8.next_cb = u8x8_utf8_next; uint16_t e_prev = 0x0ffff; uint16_t e; u8g2_uint_t delta, sum, k; u8x8_utf8_init(u8g2_GetU8x8(u8g2)); sum = 0; for(;;) { e = u8g2->u8x8.next_cb(u8g2_GetU8x8(u8g2), (uint8_t)*str); if(e == 0x0ffff) break; str++; if(e != 0x0fffe) { delta = u8g2_GetGlyphWidth(u8g2, e); if(to_left) { k = u8g2_GetKerning(u8g2, kerning, e, e_prev); delta -= k; x -= delta; } else { k = u8g2_GetKerning(u8g2, kerning, e_prev, e); delta -= k; } e_prev = e; u8g2_DrawGlyph(u8g2, x, y, e); if(to_left) { } else { x += delta; x -= k; } sum += delta; } } return sum; } u8g2_uint_t u8g2_DrawExtUTF8( u8g2_t* u8g2, u8g2_uint_t x, u8g2_uint_t y, uint8_t to_left, const uint16_t* kerning_table, const char* str) { u8g2->u8x8.next_cb = u8x8_utf8_next; uint16_t e_prev = 0x0ffff; uint16_t e; u8g2_uint_t delta, sum, k; u8x8_utf8_init(u8g2_GetU8x8(u8g2)); sum = 0; for(;;) { e = u8g2->u8x8.next_cb(u8g2_GetU8x8(u8g2), (uint8_t)*str); if(e == 0x0ffff) break; str++; if(e != 0x0fffe) { delta = u8g2_GetGlyphWidth(u8g2, e); if(to_left) { k = u8g2_GetKerningByTable(u8g2, kerning_table, e, e_prev); delta -= k; x -= delta; } else { k = u8g2_GetKerningByTable(u8g2, kerning_table, e_prev, e); delta -= k; } e_prev = e; if(to_left) { } else { x += delta; } u8g2_DrawGlyph(u8g2, x, y, e); if(to_left) { } else { //x += delta; //x -= k; } sum += delta; } } return sum; } /*===============================================*/ /* set ascent/descent for reference point calculation */ void u8g2_UpdateRefHeight(u8g2_t* u8g2) { if(u8g2->font == NULL) return; u8g2->font_ref_ascent = u8g2->font_info.ascent_A; u8g2->font_ref_descent = u8g2->font_info.descent_g; if(u8g2->font_height_mode == U8G2_FONT_HEIGHT_MODE_TEXT) { } else if(u8g2->font_height_mode == U8G2_FONT_HEIGHT_MODE_XTEXT) { if(u8g2->font_ref_ascent < u8g2->font_info.ascent_para) u8g2->font_ref_ascent = u8g2->font_info.ascent_para; if(u8g2->font_ref_descent > u8g2->font_info.descent_para) u8g2->font_ref_descent = u8g2->font_info.descent_para; } else { if(u8g2->font_ref_ascent < u8g2->font_info.max_char_height + u8g2->font_info.y_offset) u8g2->font_ref_ascent = u8g2->font_info.max_char_height + u8g2->font_info.y_offset; if(u8g2->font_ref_descent > u8g2->font_info.y_offset) u8g2->font_ref_descent = u8g2->font_info.y_offset; } } void u8g2_SetFontRefHeightText(u8g2_t* u8g2) { u8g2->font_height_mode = U8G2_FONT_HEIGHT_MODE_TEXT; u8g2_UpdateRefHeight(u8g2); } void u8g2_SetFontRefHeightExtendedText(u8g2_t* u8g2) { u8g2->font_height_mode = U8G2_FONT_HEIGHT_MODE_XTEXT; u8g2_UpdateRefHeight(u8g2); } void u8g2_SetFontRefHeightAll(u8g2_t* u8g2) { u8g2->font_height_mode = U8G2_FONT_HEIGHT_MODE_ALL; u8g2_UpdateRefHeight(u8g2); } /*===============================================*/ /* callback procedures to correct the y position */ u8g2_uint_t u8g2_font_calc_vref_font(U8X8_UNUSED u8g2_t* u8g2) { return 0; } void u8g2_SetFontPosBaseline(u8g2_t* u8g2) { u8g2->font_calc_vref = u8g2_font_calc_vref_font; } u8g2_uint_t u8g2_font_calc_vref_bottom(u8g2_t* u8g2) { return (u8g2_uint_t)(u8g2->font_ref_descent); } void u8g2_SetFontPosBottom(u8g2_t* u8g2) { u8g2->font_calc_vref = u8g2_font_calc_vref_bottom; } u8g2_uint_t u8g2_font_calc_vref_top(u8g2_t* u8g2) { u8g2_uint_t tmp; /* reference pos is one pixel above the upper edge of the reference glyph */ tmp = (u8g2_uint_t)(u8g2->font_ref_ascent); tmp++; return tmp; } void u8g2_SetFontPosTop(u8g2_t* u8g2) { u8g2->font_calc_vref = u8g2_font_calc_vref_top; } u8g2_uint_t u8g2_font_calc_vref_center(u8g2_t* u8g2) { int8_t tmp; tmp = u8g2->font_ref_ascent; tmp -= u8g2->font_ref_descent; tmp /= 2; tmp += u8g2->font_ref_descent; return tmp; } void u8g2_SetFontPosCenter(u8g2_t* u8g2) { u8g2->font_calc_vref = u8g2_font_calc_vref_center; } /*===============================================*/ void u8g2_SetFont(u8g2_t* u8g2, const uint8_t* font) { if(u8g2->font != font) { //#ifdef __unix__ // u8g2->last_font_data = NULL; // u8g2->last_unicode = 0x0ffff; //#endif u8g2->font = font; u8g2_read_font_info(&(u8g2->font_info), font); u8g2_UpdateRefHeight(u8g2); /* u8g2_SetFontPosBaseline(u8g2); */ /* removed with issue 195 */ } } /*===============================================*/ static uint8_t u8g2_is_all_valid(u8g2_t* u8g2, const char* str) U8G2_NOINLINE; static uint8_t u8g2_is_all_valid(u8g2_t* u8g2, const char* str) { uint16_t e; u8x8_utf8_init(u8g2_GetU8x8(u8g2)); for(;;) { e = u8g2->u8x8.next_cb(u8g2_GetU8x8(u8g2), (uint8_t)*str); if(e == 0x0ffff) break; str++; if(e != 0x0fffe) { if(u8g2_font_get_glyph_data(u8g2, e) == NULL) return 0; } } return 1; } uint8_t u8g2_IsAllValidUTF8(u8g2_t* u8g2, const char* str) { u8g2->u8x8.next_cb = u8x8_utf8_next; return u8g2_is_all_valid(u8g2, str); } /* string calculation is stilll not 100% perfect as it addes the initial string offset to the overall size */ static u8g2_long_t u8g2_string_width(u8g2_t* u8g2, const char* str) U8G2_NOINLINE; static u8g2_long_t u8g2_string_width(u8g2_t* u8g2, const char* str) { uint16_t e; u8g2_uint_t dx; u8g2_long_t w; u8g2->font_decode.glyph_width = 0; u8x8_utf8_init(u8g2_GetU8x8(u8g2)); /* reset the total width to zero, this will be expanded during calculation */ w = 0; dx = 0; // printf("str=<%s>\n", str); for(;;) { e = u8g2->u8x8.next_cb(u8g2_GetU8x8(u8g2), (uint8_t)*str); if(e == 0x0ffff) break; str++; if(e != 0x0fffe) { dx = u8g2_GetGlyphWidth(u8g2, e); /* delta x value of the glyph */ w += dx; } } /* adjust the last glyph, check for issue #16: do not adjust if width is 0 */ if(u8g2->font_decode.glyph_width != 0) { w -= dx; w += u8g2->font_decode .glyph_width; /* the real pixel width of the glyph, sideeffect of GetGlyphWidth */ /* issue #46: we have to add the x offset also */ w += u8g2->glyph_x_offset; /* this value is set as a side effect of u8g2_GetGlyphWidth() */ } // printf("w=%d \n", w); return w; } static void u8g2_GetGlyphHorizontalProperties( u8g2_t* u8g2, uint16_t requested_encoding, uint8_t* w, int8_t* ox, int8_t* dx) { const uint8_t* glyph_data = u8g2_font_get_glyph_data(u8g2, requested_encoding); if(glyph_data == NULL) return; u8g2_font_setup_decode(u8g2, glyph_data); *w = u8g2->font_decode.glyph_width; *ox = u8g2_font_decode_get_signed_bits(&(u8g2->font_decode), u8g2->font_info.bits_per_char_x); u8g2_font_decode_get_signed_bits(&(u8g2->font_decode), u8g2->font_info.bits_per_char_y); *dx = u8g2_font_decode_get_signed_bits(&(u8g2->font_decode), u8g2->font_info.bits_per_delta_x); } /* u8g compatible GetStrX function */ int8_t u8g2_GetStrX(u8g2_t* u8g2, const char* s) { uint8_t w; int8_t ox, dx; u8g2_GetGlyphHorizontalProperties(u8g2, *s, &w, &ox, &dx); return ox; } static u8g2_uint_t u8g2_calculate_exact_string_width(u8g2_t* u8g2, const char* str) { u8g2_uint_t w; uint16_t enc; uint8_t gw; int8_t ox, dx; /* reset the total minimal width to zero, this will be expanded during calculation */ w = 0; /* check for empty string, width is already 0 */ do { enc = u8g2->u8x8.next_cb(u8g2_GetU8x8(u8g2), (uint8_t)*str); str++; } while(enc == 0x0fffe); if(enc == 0x0ffff) return w; /* get the glyph information of the first char. This must be valid, because we already checked for the empty string */ /* if *s is not inside the font, then the cached parameters of the glyph are all zero */ u8g2_GetGlyphHorizontalProperties(u8g2, enc, &gw, &ox, &dx); /* strlen(s) == 1: width = width(s[0]) */ /* strlen(s) == 2: width = - offx(s[0]) + deltax(s[0]) + offx(s[1]) + width(s[1]) */ /* strlen(s) == 3: width = - offx(s[0]) + deltax(s[0]) + deltax(s[1]) + offx(s[2]) + width(s[2]) */ /* assume that the string has size 2 or more, than start with negative offset-x */ /* for string with size 1, this will be nullified after the loop */ w = -ox; for(;;) { /* check and stop if the end of the string is reached */ do { enc = u8g2->u8x8.next_cb(u8g2_GetU8x8(u8g2), (uint8_t)*str); str++; } while(enc == 0x0fffe); if(enc == 0x0ffff) break; u8g2_GetGlyphHorizontalProperties(u8g2, enc, &gw, &ox, &dx); /* if there are still more characters, add the delta to the next glyph */ w += dx; } /* finally calculate the width of the last char */ /* here is another exception, if the last char is a black, use the dx value instead */ if(enc != ' ') { /* if g was not updated in the for loop (strlen() == 1), then the initial offset x gets removed */ w += gw; w += ox; } else { w += dx; } return w; } u8g2_long_t u8g2_GetStrWidth(u8g2_t* u8g2, const char* s) { u8g2->u8x8.next_cb = u8x8_ascii_next; return u8g2_string_width(u8g2, s); } u8g2_uint_t u8g2_GetExactStrWidth(u8g2_t* u8g2, const char* s) { u8g2->u8x8.next_cb = u8x8_ascii_next; return u8g2_calculate_exact_string_width(u8g2, s); } /* source: https://en.wikipedia.org/wiki/UTF-8 Bits from to bytes Byte 1 Byte 2 Byte 3 Byte 4 Byte 5 Byte 6 7 U+0000 U+007F 1 0xxxxxxx 11 U+0080 U+07FF 2 110xxxxx 10xxxxxx 16 U+0800 U+FFFF 3 1110xxxx 10xxxxxx 10xxxxxx 21 U+10000 U+1FFFFF 4 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 26 U+200000 U+3FFFFFF 5 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 31 U+4000000 U+7FFFFFFF 6 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx */ u8g2_uint_t u8g2_GetUTF8Width(u8g2_t* u8g2, const char* str) { u8g2->u8x8.next_cb = u8x8_utf8_next; return u8g2_string_width(u8g2, str); } void u8g2_SetFontDirection(u8g2_t* u8g2, uint8_t dir) { #ifdef U8G2_WITH_FONT_ROTATION u8g2->font_decode.dir = dir; #endif }