Embedded SDK
Embedded SDK
Loading...
Searching...
No Matches
nbgl_draw_text.c
Go to the documentation of this file.
1
7/*********************
8 * INCLUDES
9 *********************/
10#include <string.h>
11#include "nbgl_front.h"
12#include "nbgl_draw.h"
13#include "nbgl_fonts.h"
14#include "nbgl_debug.h"
15#include "nbgl_side.h"
16#ifdef BUILD_SCREENSHOTS
17#include <assert.h>
18#endif // BUILD_SCREENSHOTS
19#include "os_pic.h"
20#include "os_utils.h"
21
22/*********************
23 * DEFINES
24 *********************/
25
26#ifdef SCREEN_SIZE_WALLET
27#define MAX_FONT_HEIGHT 54
28#define AVERAGE_CHAR_WIDTH 40
29#else // SCREEN_SIZE_WALLET
30#define MAX_FONT_HEIGHT 24
31#define AVERAGE_CHAR_WIDTH 24
32#endif // SCREEN_SIZE_WALLET
33
34// Additional height for displaying combined characters
35#define COMBINED_HEIGHT 9
36
37// Maximum number of pixels used for RLE COPY
38#define MAX_RLE_COPY_PIXELS 6
39
40/**********************
41 * TYPEDEFS
42 **********************/
43// Structure used to hold character information
44typedef struct {
45 uint32_t unicode;
46 const uint8_t *buffer;
47 uint16_t byte_cnt;
48 int16_t x_min;
49 int16_t y_min;
50 int16_t x_max;
51 int16_t y_max;
52 uint16_t width;
53 uint16_t height;
54 int16_t real_y_min;
55 uint16_t real_height;
56 uint8_t encoding;
60
62
63// Structure used to handle RLE context
64typedef struct {
65 // Part containing the data to be decoded
66 uint32_t read_cnt;
67 uint32_t buffer_len;
68 const uint8_t *buffer;
69 uint8_t byte;
70 // Part containing the decoded pixels
71 uint8_t nb_pix;
72 uint8_t color; // if color <= 15 it is a FILL, else a COPY
73 uint8_t pixels[MAX_RLE_COPY_PIXELS]; // Maximum 6 pixels (COPY)
74
76
77/**********************
78 * STATIC PROTOTYPES
79 **********************/
80/**********************
81 * STATIC VARIABLES
82 **********************/
83/**********************
84 * VARIABLES
85 **********************/
86
94static uint16_t get_bitmap_byte_cnt(const nbgl_font_t *font, uint8_t charId)
95{
96 if ((charId < font->first_char) || (charId > font->last_char)) {
97 return 0;
98 }
99
100 uint16_t baseId = charId - font->first_char;
101 if (charId < font->last_char) {
102 const nbgl_font_character_t *character
103 = (const nbgl_font_character_t *) PIC(&font->characters[baseId]);
104 const nbgl_font_character_t *nextCharacter
105 = (const nbgl_font_character_t *) PIC(&font->characters[baseId + 1]);
106 return (nextCharacter->bitmap_offset - character->bitmap_offset);
107 }
108 else if (charId == font->last_char) {
109 return (font->bitmap_len - font->characters[baseId].bitmap_offset);
110 }
111 return 0;
112}
113
114//=============================================================================
115#ifdef SCREEN_SIZE_WALLET
133static void nbgl_draw1BPPImageRle(nbgl_area_t *area,
134 const uint8_t *buffer,
135 uint32_t buffer_len,
136 nbgl_area_t *buf_area,
137 uint8_t *dst,
138 uint8_t nb_skipped_bytes)
139{
140 size_t index = 0;
141 // Set the initial number of transparent pixels
142 size_t nb_zeros = (size_t) nb_skipped_bytes * 8;
143 size_t nb_ones = 0;
144 // Width & Height are rotated 90° on Flex/Stax
145 size_t remaining_height = area->width;
146 size_t remaining_width = area->height;
147 size_t dst_index = 0;
148 uint8_t pixels;
149 uint8_t white_pixel;
150
151#ifdef BUILD_SCREENSHOTS
152 assert((buf_area->width & 7) == 0);
153 assert((buf_area->height & 7) == 0);
154#endif // BUILD_SCREENSHOTS
155
156 dst += buf_area->y0 * buf_area->width / 8;
157 dst += buf_area->x0 / 8;
158 white_pixel = 0x80 >> (buf_area->x0 & 7);
159 pixels = 0;
160
161 while (remaining_height && (index < buffer_len || nb_zeros || nb_ones)) {
162 // Reload nb_zeros & nb_ones if needed
163 while (!nb_zeros && !nb_ones && index < buffer_len) {
164 uint8_t byte = buffer[index++];
165 nb_ones = byte & 0x0F;
166 nb_zeros = byte >> 4;
167 }
168 // Get next pixel
169 if (nb_zeros) {
170 --nb_zeros;
171 // Useless, but kept for clarity
172 pixels |= 0;
173 }
174 else if (nb_ones) {
175 --nb_ones;
176 pixels |= white_pixel;
177 }
178 white_pixel >>= 1;
179 if (!white_pixel) {
180 white_pixel = 0x80;
181 dst[dst_index++] |= pixels; // OR because we handle transparency
182 pixels = 0;
183 }
184 --remaining_width;
185
186 // Have we reached the end of the line?
187 if (!remaining_width) {
188 // Width & Height are rotated 90° on Flex/Stax
189 remaining_width = area->height;
190
191 // Store current pixel content
192 dst[dst_index] |= pixels; // OR because we handle transparency
193
194 // Start next line
195 dst += buf_area->width / 8;
196 dst_index = 0;
197 pixels = 0;
198 white_pixel = 0x80 >> (buf_area->x0 & 7);
199
200 --remaining_height;
201 }
202 }
203 // Store remaining pixels
204 if (pixels) {
205 dst[dst_index] |= pixels; // OR because we handle transparency
206 }
207}
208
209// Get next pixel(s) and update Rle_context content
210static inline void get_next_pixels(rle_context_t *context, size_t remaining_width)
211{
212 // Is there still remaining data to read?
213 if (context->read_cnt >= context->buffer_len) {
214 // Just return the number of pixels to skip
215 context->nb_pix = remaining_width;
216 context->color = 0xF; // Background color, which is considered as transparent
217 return;
218 }
219
220 // Uncompress next data
221 uint8_t byte = context->buffer[context->read_cnt++];
222
223 if (byte & 0x80) {
224 if (byte & 0x40) {
225 // CMD=11 + RRRRRR => FILL White (max=63+1)
226 context->nb_pix = (byte & 0x3F) + 1;
227 context->color = 0x0F;
228 }
229 else {
230 // CMD=10 + RR + VVVV + WWWWXXXX + YYYYZZZZ + QQQQ0000 : COPY Quartets x Repeat+1
231 // - RR is repeat count - 3 of quartets (max=3+3 => 6 quartets)
232 // - VVVV: value of 1st 4BPP pixel
233 // - WWWW: value of 2nd 4BPP pixel
234 // - XXXX: value of 3rd 4BPP pixel
235 // - YYYY: value of 4th 4BPP pixel
236 // - ZZZZ: value of 5th 4BPP pixel
237 // - QQQQ: value of 6th 4BPP pixel
238 context->nb_pix = ((byte & 0x30) >> 4);
239 context->nb_pix += 3;
240 context->pixels[0] = byte & 0x0F; // Store VVVV
241 byte = context->buffer[context->read_cnt++];
242 context->pixels[1] = byte >> 4; // Store WWWW
243 context->pixels[2] = byte & 0x0F; // Store XXXX
244 if (context->nb_pix >= 4) {
245 byte = context->buffer[context->read_cnt++];
246 context->pixels[3] = byte >> 4; // Store YYYY
247 context->pixels[4] = byte & 0x0F; // Store ZZZZ
248 if (context->nb_pix >= 6) {
249 byte = context->buffer[context->read_cnt++];
250 context->pixels[5] = byte >> 4; // Store QQQQ
251 }
252 }
253 context->color = 0x10; // COPY command + pixels offset=0
254 }
255 }
256 else {
257 // CMD=0 + RRR + VVVV : FILL Value x Repeat+1 (max=7+1)
258 context->nb_pix = (byte & 0x70) >> 4;
259 context->nb_pix += 1;
260 context->color = byte & 0x0F;
261 }
262}
263
296static void nbgl_draw4BPPImageRle(nbgl_area_t *area,
297 const uint8_t *buffer,
298 uint32_t buffer_len,
299 nbgl_area_t *buf_area,
300 uint8_t *dst,
301 uint8_t nb_skipped_bytes)
302{
303 // Width & Height are rotated 90° on Flex/Stax
304 size_t remaining_height = area->width;
305 size_t remaining_width = area->height;
306 size_t dst_index = 0;
307 uint8_t dst_shift = 4; // Next pixel must be shift left 4 bit
308 rle_context_t context = {0};
309 uint8_t dst_pixel;
310
311 if (!buffer_len) {
312 return;
313 }
314
315#ifdef BUILD_SCREENSHOTS
316 assert((buf_area->width & 7) == 0);
317 assert((buf_area->height & 7) == 0);
318#endif // BUILD_SCREENSHOTS
319
320 context.buffer = buffer;
321 context.buffer_len = buffer_len;
322
323 // Handle 'transparent' pixels
324 if (nb_skipped_bytes) {
325 context.nb_pix = nb_skipped_bytes * 2;
326 context.color = 0xF; // Background color, which is considered as transparent
327 }
328
329 dst += buf_area->y0 * buf_area->width / 2;
330 dst += buf_area->x0 / 2;
331 dst_pixel = *dst;
332
333 if (buf_area->x0 & 1) {
334 dst_shift = 0;
335 }
336
337 while (remaining_height) {
338 uint8_t nb_pix;
339
340 // if the context is empty, let's fill it
341 if (context.nb_pix == 0) {
342 get_next_pixels(&context, remaining_width);
343 }
344 // Write those pixels in the RAM buffer
345 nb_pix = context.nb_pix;
346 if (nb_pix > remaining_width) {
347 nb_pix = remaining_width;
348 }
349
350 // if color <= 0x0F it is a FILL command, else it is a COPY
351 if (context.color <= 0x0F) {
352 // Do we need to just skip transparent pixels?
353 if (context.color == 0x0F) {
354 dst[dst_index] = dst_pixel;
355 dst_index += nb_pix / 2;
356 if (nb_pix & 1) {
357 dst_shift ^= 4;
358 if (dst_shift) {
359 ++dst_index;
360 }
361 }
362 dst_pixel = dst[dst_index];
363 // FILL nb_pix pixels with context.color
364 }
365 else {
366 for (uint8_t i = 0; i < nb_pix; i++) {
367 dst_pixel &= ~(0x0F << dst_shift);
368 dst_pixel |= context.color << dst_shift;
369 dst_shift ^= 4;
370 // Do we need to go to next byte?
371 if (dst_shift) {
372 dst[dst_index] = dst_pixel;
373 ++dst_index;
374 dst_pixel = dst[dst_index];
375 }
376 }
377 }
378 }
379 else {
380 uint8_t *pixels = context.pixels;
381 // LSB of context.color contains the offset of the pixels to copy
382 pixels += context.color & 0x0F;
383
384 // We can consider there is at least 1 used pixel, otherwise it would be a FILL!
385 // COPY nb_pix pixels from &context.pixels[i]
386 for (uint8_t i = 0; i < nb_pix; i++) {
387 uint8_t color = pixels[i];
388 // Handle transparency
389 if (color != 0x0F) {
390 dst_pixel &= ~(0x0F << dst_shift);
391 dst_pixel |= color << dst_shift;
392 }
393 dst_shift ^= 4;
394 // Do we need to go to next byte?
395 if (dst_shift) {
396 dst[dst_index] = dst_pixel;
397 ++dst_index;
398 dst_pixel = dst[dst_index];
399 }
400 }
401 // Update offset of the pixels to copy
402 context.color += nb_pix;
403 }
404
405 // Take in account displayed pixels
406 context.nb_pix -= nb_pix;
407 remaining_width -= nb_pix;
408
409 // Have we reached the end of the line?
410 if (remaining_width == 0) {
411 // Width & Height are rotated 90° on Flex/Stax
412 remaining_width = area->height;
413
414 // Store last pixels
415 dst[dst_index] = dst_pixel;
416 // Start next line
417 dst_index = 0;
418 dst += buf_area->width / 2;
419 dst_pixel = dst[dst_index];
420 if (buf_area->x0 & 1) {
421 dst_shift = 0;
422 }
423 else {
424 dst_shift = 4;
425 }
426 --remaining_height;
427 }
428 }
429}
430
442static void nbgl_drawImageRle(nbgl_area_t *text_area,
443 const uint8_t *buffer,
444 uint32_t buffer_len,
445 nbgl_area_t *buf_area,
446 uint8_t *dst,
447 uint8_t nb_skipped_bytes)
448{
449 if (text_area->bpp == NBGL_BPP_4) {
450 nbgl_draw4BPPImageRle(text_area, buffer, buffer_len, buf_area, dst, nb_skipped_bytes);
451 }
452 else if (text_area->bpp == NBGL_BPP_1) {
453 nbgl_draw1BPPImageRle(text_area, buffer, buffer_len, buf_area, dst, nb_skipped_bytes);
454 }
455}
456
466static void nbgl_draw4BPPImage(nbgl_area_t *area,
467 const uint8_t *buffer,
468 nbgl_area_t *buf_area,
469 uint8_t *dst)
470{
471 uint32_t buffer_len = ((area->width * area->height) + 1) / 2;
472 uint32_t index = 0;
473 // Width & Height are rotated 90° on Flex/Stax
474 size_t remaining_height = area->width;
475 size_t remaining_width = area->height;
476 size_t dst_index = 0;
477 uint8_t dst_shift = 4; // Next pixel must be shift left 4 bit
478 uint8_t dst_pixel;
479 uint8_t src_nb_pix, src_pixels[2];
480
481 if (!buffer_len) {
482 return;
483 }
484
485#ifdef BUILD_SCREENSHOTS
486 assert((buf_area->width & 7) == 0);
487 assert((buf_area->height & 7) == 0);
488#endif // BUILD_SCREENSHOTS
489
490 dst += buf_area->y0 * buf_area->width / 2;
491 dst += buf_area->x0 / 2;
492 dst_pixel = *dst;
493
494 if (buf_area->x0 & 1) {
495 dst_shift = 0;
496 }
497 src_pixels[0] = buffer[index++];
498 src_pixels[1] = src_pixels[0] >> 4;
499 src_pixels[0] &= 0x0F;
500 src_nb_pix = 2;
501
502 while (remaining_height) {
503 uint8_t nb_pix;
504
505 // refill data if ncecessary
506 if (src_nb_pix == 0) {
507 if (index < buffer_len) {
508 src_pixels[0] = buffer[index++];
509 src_pixels[1] = src_pixels[0] >> 4;
510 src_pixels[0] &= 0x0F;
511 src_nb_pix = 2;
512 }
513 else {
514 // We have no more src data => fill with transparent color
515 // Background color, which is considered as transparent
516 src_pixels[0] = 0x0F;
517 src_pixels[1] = 0x0F;
518 src_nb_pix = remaining_width;
519 }
520 }
521 // Write those pixels in the RAM buffer
522 nb_pix = src_nb_pix;
523 if (nb_pix > remaining_width) {
524 nb_pix = remaining_width;
525 }
526
527 for (uint8_t i = 0; i < nb_pix; i++) {
528 uint8_t color = src_pixels[i];
529 // Handle transparency
530 if (color != 0x0F) {
531 dst_pixel &= ~(0x0F << dst_shift);
532 dst_pixel |= color << dst_shift;
533 }
534 dst_shift ^= 4;
535 // Do we need to go to next byte?
536 if (dst_shift) {
537 dst[dst_index] = dst_pixel;
538 ++dst_index;
539 dst_pixel = dst[dst_index];
540 }
541 }
542
543 // Take in account displayed pixels
544 src_nb_pix -= nb_pix;
545 remaining_width -= nb_pix;
546
547 // Have we reached the end of the line?
548 if (remaining_width == 0) {
549 // Width & Height are rotated 90° on Flex/Stax
550 remaining_width = area->height;
551
552 // Store last pixels
553 dst[dst_index] = dst_pixel;
554 // Start next line
555 dst_index = 0;
556 dst += buf_area->width / 2;
557 dst_pixel = dst[dst_index];
558 if (buf_area->x0 & 1) {
559 dst_shift = 0;
560 }
561 else {
562 dst_shift = 4;
563 }
564 --remaining_height;
565 }
566 }
567}
568
578static void nbgl_draw1BPPImage(nbgl_area_t *area,
579 const uint8_t *buffer,
580 nbgl_area_t *buf_area,
581 uint8_t *dst)
582{
583 size_t index = 0;
584 uint32_t buffer_len = ((area->width * area->height) + 7) / 8;
585 // Width & Height are rotated 90° on Flex/Stax
586 size_t remaining_height = area->width;
587 size_t remaining_width = area->height;
588 size_t dst_index = 0;
589 uint8_t pixels;
590 uint8_t white_pixel;
591 uint8_t src_byte, src_msk;
592
593#ifdef BUILD_SCREENSHOTS
594 assert((buf_area->width & 7) == 0);
595 assert((buf_area->height & 7) == 0);
596#endif // BUILD_SCREENSHOTS
597
598 dst += buf_area->y0 * buf_area->width / 8;
599 dst += buf_area->x0 / 8;
600 white_pixel = 0x80 >> (buf_area->x0 & 7);
601 pixels = 0;
602
603 src_byte = buffer[index++];
604 src_msk = 0x80;
605
606 while (remaining_height && (index < buffer_len || src_msk)) {
607 // Reload data if needed
608 if (!src_msk && index < buffer_len) {
609 src_byte = buffer[index++];
610 src_msk = 0x80;
611 }
612 // Get next pixel
613 if (src_byte & src_msk) {
614 pixels |= white_pixel;
615 }
616 else {
617 // Useless, but kept for clarity
618 pixels |= 0;
619 }
620 src_msk >>= 1;
621 white_pixel >>= 1;
622 if (!white_pixel) {
623 white_pixel = 0x80;
624 dst[dst_index++] |= pixels; // OR because we handle transparency
625 pixels = 0;
626 }
627 --remaining_width;
628
629 // Have we reached the end of the line?
630 if (!remaining_width) {
631 // Width & Height are rotated 90° on Flex/Stax
632 remaining_width = area->height;
633
634 // Store current pixel content
635 dst[dst_index] |= pixels; // OR because we handle transparency
636
637 // Start next line
638 dst += buf_area->width / 8;
639 dst_index = 0;
640 pixels = 0;
641 white_pixel = 0x80 >> (buf_area->x0 & 7);
642
643 --remaining_height;
644 }
645 }
646 // Store remaining pixels
647 if (pixels) {
648 dst[dst_index] |= pixels; // OR because we handle transparency
649 }
650}
651
660static void nbgl_drawImage(nbgl_area_t *text_area,
661 const uint8_t *buffer,
662 nbgl_area_t *buf_area,
663 uint8_t *dst)
664{
665 if (text_area->bpp == NBGL_BPP_4) {
666 nbgl_draw4BPPImage(text_area, buffer, buf_area, dst);
667 }
668 else if (text_area->bpp == NBGL_BPP_1) {
669 nbgl_draw1BPPImage(text_area, buffer, buf_area, dst);
670 }
671}
672
681static void pack_ram_buffer(nbgl_area_t *area, uint16_t width, uint16_t height)
682{
683 uint8_t *src = ramBuffer;
684 uint8_t *dst = ramBuffer;
685
686 if (area->bpp == NBGL_BPP_4) {
687 uint16_t bytes_per_line;
688 uint16_t skip;
689
690 src += area->y0 * area->width / 2;
691 src += area->x0 / 2;
692 bytes_per_line = (width + 1) / 2;
693 skip = (area->width + 1) / 2;
694
695 // Do we need to copy quartet by quartet, or can we copy byte by byte?
696 if (area->x0 & 1) {
697 // We need to copy quartet by quartet
698 skip -= bytes_per_line;
699 for (uint16_t h = 0; h < height; h++) {
700 for (uint16_t w = 0; w < bytes_per_line; w++) {
701 uint8_t byte = *src++ & 0x0F; // 1st quartet
702 byte <<= 4;
703 byte |= *src >> 4;
704 *dst++ = byte;
705 }
706 src += skip;
707 }
708 }
709 else {
710 for (uint16_t y = 0; y < height; y++) {
711 memmove(dst, src, bytes_per_line);
712 dst += bytes_per_line;
713 src += skip;
714 }
715 }
716 }
717 else {
718 uint8_t src_pixel;
719 uint8_t src_shift;
720 uint8_t src_index;
721 uint8_t dst_pixel = 0;
722 uint8_t dst_shift = 7;
723
724 src += area->y0 * area->width / 8;
725 src += area->x0 / 8;
726 for (uint16_t y = 0; y < height; y++) {
727 src_shift = (7 - (area->x0 & 7));
728 src_pixel = *src;
729 src_index = 0;
730 for (uint16_t x = 0; x < width; x++) {
731 dst_pixel |= ((src_pixel >> src_shift) & 1) << dst_shift;
732 if (dst_shift == 0) {
733 dst_shift = 8;
734 *dst++ = dst_pixel;
735 dst_pixel = 0;
736 }
737 --dst_shift;
738
739 if (src_shift == 0) {
740 src_shift = 8;
741 ++src_index;
742 src_pixel = src[src_index];
743 }
744 --src_shift;
745 }
746 src += area->width / 8;
747 }
748 // Write last byte, if any
749 if (dst_shift != 7) {
750 *dst++ = dst_pixel;
751 }
752 }
753}
754
763static void display_ram_buffer(int16_t x_min,
764 int16_t y_min,
765 int16_t x_max,
766 int16_t y_max,
767 const nbgl_area_t *area,
768 nbgl_area_t *buf_area,
769 nbgl_area_t *char_area,
770 color_t fontColor)
771{
772 // Move the data at the beginning of RAM buffer, in a packed way
773 buf_area->x0 = x_min;
774 buf_area->y0 = y_min;
775 char_area->height = ((x_max - x_min) + 3) & 0xFFFC;
776 char_area->width = y_max - y_min;
777
778 pack_ram_buffer(buf_area, char_area->height, char_area->width);
779
780 char_area->y0 = area->y0 + x_min - COMBINED_HEIGHT;
781
782 nbgl_frontDrawImage(char_area, ramBuffer, NO_TRANSFORMATION, fontColor);
783}
784#endif // SCREEN_SIZE_WALLET
785
786static void update_char_info(character_info_t *char_info,
787 const uint8_t **text,
788 uint16_t *textLen,
789 nbgl_unicode_ctx_t *unicode_ctx,
790 const nbgl_font_t *font)
791{
792 char_info->unicode = nbgl_popUnicodeChar(text, textLen, &char_info->is_unicode);
793
794 // Do we still have some characters to read?
795 if (!char_info->unicode) {
796 return;
797 }
798
799 // Retrieves information depending on whether it is an ASCII character or not.
800 if (char_info->is_unicode) {
801#ifdef HAVE_UNICODE_SUPPORT
802 const nbgl_font_unicode_character_t *unicodeCharacter
803 = nbgl_getUnicodeFontCharacter(char_info->unicode);
804 // if not supported char, go to next one (this should never happen!!)
805 if (unicodeCharacter == NULL) {
806#ifdef BUILD_SCREENSHOTS
807 fprintf(stdout,
808 "Inside update_char_info, unicode (%c)[0x%X] is not supported!\n",
809 char_info->unicode,
810 char_info->unicode);
811#endif // BUILD_SCREENSHOTS
812 update_char_info(char_info, text, textLen, unicode_ctx, font);
813 return;
814 }
815 char_info->width = unicodeCharacter->width;
816#if defined(HAVE_LANGUAGE_PACK)
817 char_info->buffer = unicode_ctx->bitmap;
818 char_info->buffer += unicodeCharacter->bitmap_offset;
819
820 char_info->x_max = char_info->width;
821 char_info->y_max = unicode_ctx->font->height;
822#ifdef SCREEN_SIZE_WALLET
823 char_info->over_previous = unicodeCharacter->over_previous;
824#else // SCREEN_SIZE_WALLET
825 char_info->over_previous = 0;
826#endif // SCREEN_SIZE_WALLET
827
828 if (!unicode_ctx->font->crop) {
829 // Take in account the skipped bytes, if any
830 char_info->nb_skipped_bytes = (unicodeCharacter->x_min_offset & 7) << 3;
831 char_info->nb_skipped_bytes |= unicodeCharacter->y_min_offset & 7;
832 char_info->x_min = 0;
833 char_info->y_min = 0;
834 }
835 else {
836 char_info->nb_skipped_bytes = 0;
837 char_info->y_min = unicode_ctx->font->y_min;
838 char_info->y_min += (uint16_t) unicodeCharacter->y_min_offset;
839 char_info->y_max -= (uint16_t) unicodeCharacter->y_max_offset;
840
841 if (char_info->over_previous) {
842 // That character will be displayed over the previous one: get correct X coords
843 char_info->x_min = -(int16_t) unicodeCharacter->width;
844 char_info->width = 16 * (uint16_t) unicodeCharacter->x_min_offset;
845 char_info->width += (uint16_t) unicodeCharacter->x_max_offset;
846 char_info->x_max = char_info->x_min + char_info->width;
847 }
848 else {
849 char_info->x_min = (uint16_t) unicodeCharacter->x_min_offset;
850 char_info->x_max -= (uint16_t) unicodeCharacter->x_max_offset;
851 }
852 }
853 char_info->byte_cnt = nbgl_getUnicodeFontCharacterByteCount();
854 char_info->encoding = unicodeCharacter->encoding;
855 char_info->height = char_info->y_max - char_info->y_min;
856#endif // defined(HAVE_LANGUAGE_PACK)
857#else // HAVE_UNICODE_SUPPORT
858 return;
859#endif // HAVE_UNICODE_SUPPORT
860 }
861 else {
862 // Special character: nothing special to do here
863 if (char_info->unicode == '\f' || char_info->unicode == '\b') {
864 return;
865 }
866 // if not supported char, go to next one (this should never happen!!)
867 if ((char_info->unicode < font->first_char) || (char_info->unicode > font->last_char)) {
868#ifdef BUILD_SCREENSHOTS
869 fprintf(stdout,
870 "Inside update_char_info, unicode (%c)[0x%X] is not supported!\n",
871 char_info->unicode,
872 char_info->unicode);
873#endif // BUILD_SCREENSHOTS
874 update_char_info(char_info, text, textLen, unicode_ctx, font);
875 return;
876 }
877 const nbgl_font_character_t *character = (const nbgl_font_character_t *) PIC(
878 &font->characters[char_info->unicode - font->first_char]);
879
880 char_info->buffer = (const uint8_t *) PIC(&font->bitmap[character->bitmap_offset]);
881 char_info->width = character->width;
882 char_info->encoding = character->encoding;
883 char_info->over_previous = 0;
884
885 char_info->x_max = char_info->width;
886 char_info->y_max = font->height;
887
888 if (!font->crop) {
889 // Take in account the skipped bytes, if any
890 char_info->nb_skipped_bytes = (character->x_min_offset & 7) << 3;
891 char_info->nb_skipped_bytes |= character->y_min_offset & 7;
892 char_info->x_min = 0;
893 char_info->y_min = 0;
894 }
895 else {
896 char_info->nb_skipped_bytes = 0;
897 char_info->x_min = (uint16_t) character->x_min_offset;
898 char_info->y_min = font->y_min;
899 char_info->y_min += (uint16_t) character->y_min_offset;
900 char_info->x_max -= (uint16_t) character->x_max_offset;
901 char_info->y_max -= (uint16_t) character->y_max_offset;
902 }
903
904 char_info->byte_cnt = get_bitmap_byte_cnt(font, char_info->unicode);
905 char_info->height = char_info->y_max - char_info->y_min;
906 }
907}
908
919 const char *text,
920 uint16_t textLen,
921 nbgl_font_id_e fontId,
922 color_t fontColor)
923{
924 // text is a series of characters, each character being a bitmap
925 // we need to align bitmaps on width multiple of 4 limitation.
926 int16_t x = area->x0;
927 nbgl_area_t current_area;
928 character_info_t current_char = {0};
929 const nbgl_font_t *font = nbgl_getFont(fontId);
930#ifdef HAVE_UNICODE_SUPPORT
931 nbgl_unicode_ctx_t *unicode_ctx = nbgl_getUnicodeFont(fontId);
932#else // HAVE_UNICODE_SUPPORT
933 nbgl_unicode_ctx_t *unicode_ctx = NULL;
934#endif // HAVE_UNICODE_SUPPORT
935#ifdef SCREEN_SIZE_WALLET
936 int16_t next_x = x;
937 character_info_t previous_char = {0};
938 character_info_t next_char = {0};
939 nbgl_area_t previous_area;
940 int16_t buf_x_min;
941 int16_t buf_y_min;
942 int16_t buf_x_max;
943 int16_t buf_y_max;
944 // Flag set to 1 when there is data to be drawn in RAM buffer
945 uint8_t redraw_buf_area = 0;
946
947 // Area representing the RAM buffer and in which the glyphs will be drawn
948 nbgl_area_t buf_area = {0};
949 // ensure that the ramBuffer also used for image file decompression is big enough
950 // 4bpp: size of ram_buffer is (font->height * 3*AVERAGE_CHAR_WIDTH/2) / 2
951 // 1bpp: size of ram_buffer is (font->height * 3*AVERAGE_CHAR_WIDTH/2) / 8
952 CCASSERT(ram_buffer,
954 // TODO Investigate why area->bpp is not always initialized correctly
955 buf_area.bpp = (nbgl_bpp_t) font->bpp;
956 // Width & Height are rotated 90° on Flex/Stax
957#ifdef HAVE_UNICODE_SUPPORT
958 buf_area.width = (unicode_ctx->font->line_height + 7) & 0xFFF8; // Modulo 8 is better for 1BPP
959#else // HAVE_UNICODE_SUPPORT
960 buf_area.width = (MAX_FONT_HEIGHT + 7) & 0xFFF8; // Modulo 8 is better for 1BPP
961#endif // HAVE_UNICODE_SUPPORT
962 buf_area.height = ((3 * AVERAGE_CHAR_WIDTH / 2) + 7) & 0xFFF8;
963 if (buf_area.bpp == NBGL_BPP_4) {
964 buf_area.backgroundColor = 0xF; // This will be the transparent color
965 }
966 else {
967 buf_area.backgroundColor = 0; // This will be the transparent color
968 }
969#ifdef BUILD_SCREENSHOTS
970 assert((buf_area.height * buf_area.width / 2) <= GZLIB_UNCOMPRESSED_CHUNK);
971#endif // BUILD_SCREENSHOTS
972
973 // Those variables will be updated with current_char dimension
974 buf_x_min = AVERAGE_CHAR_WIDTH;
975 buf_y_min = MAX_FONT_HEIGHT;
976 buf_x_max = 0;
977 buf_y_max = 0;
978#endif // SCREEN_SIZE_WALLET
979
981 "nbgl_drawText: x0 = %d, y0 = %d, w = %d, h = %d, fontColor = %d, "
982 "backgroundColor=%d, text = %s\n",
983 area->x0,
984 area->y0,
985 area->width,
986 area->height,
987 fontColor,
988 area->backgroundColor,
989 text);
990
991 current_area.backgroundColor = area->backgroundColor;
992 current_area.bpp = (nbgl_bpp_t) font->bpp;
993
994#ifndef SCREEN_SIZE_WALLET
995 while (textLen > 0) {
996 // Get info for current character
997 update_char_info(&current_char, (const uint8_t **) &text, &textLen, unicode_ctx, font);
998
999 // Handle special characters (for LNX & LNS+ devices)
1000 if (!current_char.is_unicode) {
1001 // If '\f', that string si completed
1002 if (current_char.unicode == '\f') {
1003 break;
1004 }
1005 // if \b, switch fontId
1006 if (current_char.unicode == '\b') {
1007 if (fontId == BAGL_FONT_OPEN_SANS_REGULAR_11px_1bpp) { // switch to bold
1009#ifdef HAVE_UNICODE_SUPPORT
1010 unicode_ctx = nbgl_getUnicodeFont(fontId);
1011#endif // HAVE_UNICODE_SUPPORT
1012 font = (const nbgl_font_t *) nbgl_getFont(fontId);
1013 }
1014 else if (fontId == BAGL_FONT_OPEN_SANS_EXTRABOLD_11px_1bpp) { // switch to regular
1016#ifdef HAVE_UNICODE_SUPPORT
1017 unicode_ctx = nbgl_getUnicodeFont(fontId);
1018#endif // HAVE_UNICODE_SUPPORT
1019 font = (const nbgl_font_t *) nbgl_getFont(fontId);
1020 }
1021 continue;
1022 }
1023 }
1024
1025 current_area.x0 = x + current_char.x_min;
1026 current_area.y0 = area->y0 + current_char.y_min;
1027 current_area.height = (current_char.y_max - current_char.y_min);
1028 current_area.width = (current_char.x_max - current_char.x_min);
1029
1030 // If char_byte_cnt = 0, call nbgl_frontDrawImageRle to let speculos notice
1031 // a space character was 'displayed'
1032 if (!current_char.byte_cnt || current_char.encoding == 1) {
1033 // Draw that character in the RAM buffer, with transparency
1034 nbgl_frontDrawImageRle(&current_area,
1035 current_char.buffer,
1036 current_char.byte_cnt,
1037 fontColor,
1038 current_char.nb_skipped_bytes);
1039 // WARNING: current_area was adjusted to height of what was really displayed!
1040 }
1041 else {
1042 nbgl_frontDrawImage(&current_area, current_char.buffer, NO_TRANSFORMATION, fontColor);
1043 }
1044 x += current_char.width - font->char_kerning;
1045 }
1046#else // SCREEN_SIZE_WALLET
1047 // Get the first character, that will go into 'current_char'
1048 // (to correctly handle languages using 'combined characters', we need to
1049 // know current, previous & next characters)
1050 update_char_info(&next_char, (const uint8_t **) &text, &textLen, unicode_ctx, font);
1051
1052 while (textLen > 0 || next_char.unicode) {
1053 // Get the character we already read
1054 current_char = next_char;
1055 // Get info for next character
1056 update_char_info(&next_char, (const uint8_t **) &text, &textLen, unicode_ctx, font);
1057
1058 // Render current character in current_area
1059 if (!current_char.over_previous) {
1060 // This character will not be displayed over previous one => update x
1061 if (next_x > x) {
1062 x = next_x;
1063 }
1064 current_area.x0 = x + current_char.x_min;
1065 }
1066 current_area.y0 = area->y0 + current_char.y_min;
1067 current_area.height = (current_char.y_max - current_char.y_min);
1068 current_area.width = (current_char.x_max - current_char.x_min);
1069
1070 // if current character should not be displayed over previous one,
1071 // send RAM buffer to display
1072 if (!current_char.over_previous) {
1073 // Send RAM buffer content (previous character) to display
1074 if (redraw_buf_area) {
1075 display_ram_buffer(buf_x_min,
1076 buf_y_min,
1077 buf_x_max,
1078 buf_y_max,
1079 area,
1080 &buf_area,
1081 &previous_area,
1082 fontColor);
1083 // Reset that flag
1084 redraw_buf_area = 0;
1085 buf_x_min = AVERAGE_CHAR_WIDTH;
1086 buf_y_min = MAX_FONT_HEIGHT;
1087 buf_x_max = 0;
1088 buf_y_max = 0;
1089 }
1090 // To handle transparency, ramBuffer must be filled with background color
1091 // => Fill ramBuffer with background color (0x0F for 4bpp & 0 for 1bpp)
1092 if (buf_area.bpp == NBGL_BPP_4) {
1093 memset(ramBuffer, 0xFF, (buf_area.height * buf_area.width / 2));
1094 }
1095 else {
1096 memset(ramBuffer, 0x0, (buf_area.height * buf_area.width / 8));
1097 }
1098 // Update display coordinates of current char inside the RAM buffer
1099 //(X & Y are rotated 90° on Stax/Flex)
1100 buf_area.x0 = COMBINED_HEIGHT + current_char.y_min;
1101 buf_area.y0 = (AVERAGE_CHAR_WIDTH / 2) + current_char.x_min;
1102
1103 buf_x_min = buf_area.x0;
1104 buf_x_max = buf_x_min + current_area.height;
1105 buf_y_min = buf_area.y0;
1106 buf_y_max = buf_y_min + current_area.width;
1107
1108 // Compute next x with current char width, to be ready to display next one
1109 x += current_char.width - font->char_kerning;
1110 next_x = x;
1111 }
1112 else {
1113 // Security check: first character can't be a composed one!
1114 if (previous_char.unicode == 0) {
1115#ifdef BUILD_SCREENSHOTS
1116 fprintf(stdout,
1117 "WARNING: First character '%c'(0x%X) is a composed one! (text=>%s<=)\n",
1118 current_char.unicode,
1119 current_char.unicode,
1120 text);
1121#endif // BUILD_SCREENSHOTS
1122 continue;
1123 }
1124 // Update next_x if this character is larger than original one
1125 // (except for vowel 0xE31 which is overflowing on purpose on right side)
1126 if (current_char.unicode != 0x00E31
1127 && (x + current_char.x_min + current_char.width) > next_x) {
1128 next_x = x + current_char.x_min + current_char.width;
1129 // That character is a special one, as it is displayed on top of previous
1130 // character and also on its right => give space for next character
1131 if (current_char.unicode == 0x00E33) {
1132 next_x += 1;
1133 }
1134 }
1135 // Take in account current x_min (which is < 0)
1136 buf_area.x0 = COMBINED_HEIGHT + current_char.y_min;
1137 buf_area.y0 = (AVERAGE_CHAR_WIDTH / 2) - current_char.x_min - current_char.width;
1138
1139 // Thai rules for displaying characters on top of each others
1140 // Order priority, from bottom to top
1141 // 1 - consonnant
1142 // 2 - vowel or sign 0x0E4D and 0x0E4E
1143 // 3 - tone 0x0E48, 0x0E49, 0x0E4A and 0x0E4B or sign 0x0E4C
1144 // => 0x0E48, 0x0E49, 0x0E4A, 0x0E4B & 0x0E4C MUST ALWAYS be displayed on top of
1145 // other characters! WARNING: some vowels, signs or tone may have to be displayed
1146 // left with some consonnant (0E1B, 0E1F etc)
1147
1148 if (current_char.unicode >= 0x0E48 && current_char.unicode <= 0x0E4C) {
1149 if (next_char.unicode == 0x00E33) {
1150 // Display current character up to next one
1151 buf_area.x0 = COMBINED_HEIGHT + 0;
1152 buf_area.x0
1153 -= (current_char.y_max - current_char.y_min) - 2; // minus height of cur
1154 // char TMP
1155 }
1156 else if (previous_char.unicode >= 0x0E31 && previous_char.unicode <= 0x0E37) {
1157 // Take in account the height of previous character
1158 buf_area.x0 = buf_x_min - (current_char.y_max - current_char.y_min) - 1;
1159 }
1160 else if (current_char.unicode == 0x0E4B && previous_char.unicode == 0x0E1B) {
1161 // We must shift 0x0E4B to the left or we will overwrite 0x0E1B
1162 buf_area.y0 -= current_char.x_min / 2;
1163 }
1164 }
1165 // Update RAM buffer x/y min/max
1166 if (buf_area.x0 < buf_x_min) {
1167 buf_x_min = buf_area.x0;
1168 }
1169 if ((buf_area.x0 + current_area.height) > buf_x_max) {
1170 buf_x_max = buf_area.x0 + current_area.height;
1171 }
1172 if (buf_area.y0 < buf_y_min) {
1173 buf_y_min = buf_area.y0;
1174 }
1175 if ((buf_area.y0 + current_area.width) > buf_y_max) {
1176 buf_y_max = buf_area.y0 + current_area.width;
1177 }
1178 }
1179 // If char_byte_cnt = 0, call nbgl_frontDrawImageRle to let speculos notice
1180 // a space character was 'displayed'
1181 if (!current_char.byte_cnt || current_char.encoding == 1) {
1182 // Draw that character in the RAM buffer, with transparency
1183 nbgl_drawImageRle(&current_area,
1184 current_char.buffer,
1185 current_char.byte_cnt,
1186 &buf_area,
1187 ramBuffer,
1188 current_char.nb_skipped_bytes);
1189 }
1190 else {
1191 nbgl_drawImage(&current_area, current_char.buffer, &buf_area, ramBuffer);
1192 }
1193
1194 // Set the flag telling that RAM buffer need to be displayed, if needed
1195 if (current_char.byte_cnt) {
1196 redraw_buf_area = 1;
1197 previous_area = current_area;
1198 }
1199
1200 previous_char = current_char;
1201 }
1202 // Do we need to send RAM buffer content (previous character) to display?
1203 if (redraw_buf_area) {
1204 // Move the data at the beginning of RAM buffer, in a packed way
1205 display_ram_buffer(
1206 buf_x_min, buf_y_min, buf_x_max, buf_y_max, area, &buf_area, &previous_area, fontColor);
1207 }
1208#endif // SCREEN_SIZE_WALLET
1209 return fontId;
1210}
debug traces management
#define LOG_DEBUG(__logger,...)
Definition nbgl_debug.h:86
@ DRAW_LOGGER
Definition nbgl_debug.h:29
CCASSERT(qr_code_buffer, sizeof(QrCodeBuffer_t)<=GZLIB_UNCOMPRESSED_CHUNK)
Middle Level API of the new BOLOS Graphical Library.
nbgl_font_id_e nbgl_drawText(const nbgl_area_t *area, const char *text, uint16_t textLen, nbgl_font_id_e fontId, color_t fontColor)
This function draws the given single-line text, with the given parameters.
#define COMBINED_HEIGHT
#define AVERAGE_CHAR_WIDTH
#define MAX_FONT_HEIGHT
#define MAX_RLE_COPY_PIXELS
uint32_t nbgl_popUnicodeChar(const uint8_t **text, uint16_t *text_length, bool *is_unicode)
Get the coming unicode value on the given UTF-8 string. If the value is a simple ASCII character,...
Definition nbgl_fonts.c:145
nbgl_font_id_e
Definition nbgl_fonts.h:144
@ BAGL_FONT_OPEN_SANS_REGULAR_11px_1bpp
Definition nbgl_fonts.h:153
@ BAGL_FONT_OPEN_SANS_EXTRABOLD_11px_1bpp
Definition nbgl_fonts.h:151
const nbgl_font_t * nbgl_getFont(nbgl_font_id_e fontId)
Font screen low-Level driver API, to draw elementary forms.
void nbgl_frontDrawImage(const nbgl_area_t *area, const uint8_t *buffer, nbgl_transformation_t transformation, nbgl_color_map_t colorMap)
void nbgl_frontDrawImageRle(const nbgl_area_t *area, const uint8_t *buffer, uint32_t buffer_len, color_t fore_color, uint8_t nb_skipped_bytes)
Side screen low-Level driver API, to draw elementary forms.
uint8_t ramBuffer[]
Definition nbgl_obj.c:146
color_t
Definition nbgl_types.h:140
#define GZLIB_UNCOMPRESSED_CHUNK
size of gzlib uncompression buffer in bytes
Definition nbgl_types.h:305
#define NO_TRANSFORMATION
Definition nbgl_types.h:91
nbgl_bpp_t
Enum to represent the number of bits per pixel (BPP)
Definition nbgl_types.h:283
@ NBGL_BPP_1
1 bit per pixel
Definition nbgl_types.h:284
@ NBGL_BPP_4
4 bits per pixel
Definition nbgl_types.h:286
struct PACKED__ nbgl_area_s nbgl_area_t
Represents a rectangle area of the screen.
const uint8_t * buffer
fonts nicknames to be used for various wallet size targets (non-Nano)
Definition nbgl_fonts.h:69
uint32_t width
width of character in pixels
Definition nbgl_fonts.h:72
uint32_t encoding
method used to encode bitmap data
Definition nbgl_fonts.h:71
uint32_t x_min_offset
x_min = x_min_offset
Definition nbgl_fonts.h:73
uint32_t bitmap_offset
offset of this character in chars buffer
Definition nbgl_fonts.h:70
uint32_t y_min_offset
y_min = (y_min + y_min_offset)
Definition nbgl_fonts.h:74
uint32_t x_max_offset
x_max = width - x_max_offset
Definition nbgl_fonts.h:75
uint32_t y_max_offset
y_max = (height - y_max_offset)
Definition nbgl_fonts.h:76
structure defining an ASCII font
Definition nbgl_fonts.h:83
uint8_t char_kerning
kerning for the font
Definition nbgl_fonts.h:89
uint8_t crop
If false, x_min_offset+y_min_offset=bytes to skip.
Definition nbgl_fonts.h:90
uint8_t first_char
ASCII code of the first character in bitmap and in characters fields.
Definition nbgl_fonts.h:93
uint32_t bitmap_len
Size in bytes of the associated bitmap.
Definition nbgl_fonts.h:84
uint8_t const * bitmap
array containing bitmaps of all characters
Definition nbgl_fonts.h:98
const nbgl_font_character_t *const characters
array containing definitions of all characters
Definition nbgl_fonts.h:97
uint8_t height
height of all characters in pixels
Definition nbgl_fonts.h:87
uint8_t last_char
ASCII code of the last character in bitmap and in characters fields.
Definition nbgl_fonts.h:95
uint8_t y_min
Most top Y coordinate of any char in the font.
Definition nbgl_fonts.h:91
uint8_t bpp
number of bits per pixels
Definition nbgl_fonts.h:86
structure defining a unicode character (except the bitmap)
Definition nbgl_fonts.h:111
uint32_t width
width of character in pixels
Definition nbgl_fonts.h:114
uint32_t x_min_offset
x_min = x_min_offset
Definition nbgl_fonts.h:115
uint32_t y_min_offset
y_min = (y_min + y_min_offset)
Definition nbgl_fonts.h:116
uint32_t y_max_offset
y_max = (height - y_max_offset)
Definition nbgl_fonts.h:118
uint32_t over_previous
flag set to 1 when displayed over previous char
Definition nbgl_fonts.h:120
uint32_t x_max_offset
x_max = width - x_max_offset
Definition nbgl_fonts.h:117
uint32_t bitmap_offset
offset of this character in chars buffer
Definition nbgl_fonts.h:119
uint8_t crop
If false, x_min_offset+y_min_offset=bytes to skip.
Definition nbgl_fonts.h:133
uint8_t y_min
Most top Y coordinate of any char in the font.
Definition nbgl_fonts.h:134
uint8_t height
height of all characters in pixels
Definition nbgl_fonts.h:130
uint8_t line_height
height of a line for all characters in pixels
Definition nbgl_fonts.h:131
const nbgl_font_unicode_t * font
Definition nbgl_fonts.h:167
const uint8_t * bitmap
Definition nbgl_fonts.h:169
uint8_t pixels[MAX_RLE_COPY_PIXELS]
uint32_t buffer_len
const uint8_t * buffer
uint32_t read_cnt