Embedded SDK
Embedded SDK
Loading...
Searching...
No Matches
ux_layout_paging_compute.c
Go to the documentation of this file.
1#include "os_helpers.h"
2#include "os_pic.h"
3#include "ux.h"
4#include "ux_layouts.h"
5#include "bagl.h"
6#include "string.h"
7#include "os.h"
8
9#ifdef HAVE_UX_FLOW
10#ifdef HAVE_BAGL
11
12#if !defined(HAVE_SE_SCREEN)
13
14// We implement a light mechanism in order to be able to retrieve the width of
15// nano S characters, in the two possible fonts:
16// - BAGL_FONT_OPEN_SANS_EXTRABOLD_11px,
17// - BAGL_FONT_OPEN_SANS_REGULAR_11px.
18#define NANOS_FIRST_CHAR 0x20
19#define NANOS_LAST_CHAR 0x7F
20
21// OPEN_SANS_REGULAR_11PX << 4 | OPEN_SANS_EXTRABOLD_11PX
22// Check with files (in the bagl_font_character_t array):
23// - common/bagl/src/bagl_font_open_sans_extrabold_11px.inc
24// - common/bagl/src/bagl_font_open_sans_regular_11px.inc
25const char nanos_characters_width[96] = {
26 3 << 4 | 3, /* code 0020 */
27 3 << 4 | 3, /* code 0021 */
28 4 << 4 | 6, /* code 0022 */
29 7 << 4 | 7, /* code 0023 */
30 6 << 4 | 6, /* code 0024 */
31 9 << 4 | 10, /* code 0025 */
32 8 << 4 | 9, /* code 0026 */
33 2 << 4 | 3, /* code 0027 */
34 3 << 4 | 4, /* code 0028 */
35 3 << 4 | 4, /* code 0029 */
36 6 << 4 | 6, /* code 002A */
37 6 << 4 | 6, /* code 002B */
38 3 << 4 | 3, /* code 002C */
39 4 << 4 | 4, /* code 002D */
40 3 << 4 | 3, /* code 002E */
41 4 << 4 | 5, /* code 002F */
42 6 << 4 | 8, /* code 0030 */
43 6 << 4 | 6, /* code 0031 */
44 6 << 4 | 7, /* code 0032 */
45 6 << 4 | 7, /* code 0033 */
46 8 << 4 | 8, /* code 0034 */
47 6 << 4 | 6, /* code 0035 */
48 6 << 4 | 8, /* code 0036 */
49 6 << 4 | 7, /* code 0037 */
50 6 << 4 | 8, /* code 0038 */
51 6 << 4 | 8, /* code 0039 */
52 3 << 4 | 3, /* code 003A */
53 3 << 4 | 3, /* code 003B */
54 6 << 4 | 5, /* code 003C */
55 6 << 4 | 6, /* code 003D */
56 6 << 4 | 5, /* code 003E */
57 5 << 4 | 6, /* code 003F */
58 10 << 4 | 10, /* code 0040 */
59 7 << 4 | 8, /* code 0041 */
60 7 << 4 | 7, /* code 0042 */
61 7 << 4 | 7, /* code 0043 */
62 8 << 4 | 8, /* code 0044 */
63 6 << 4 | 6, /* code 0045 */
64 6 << 4 | 6, /* code 0046 */
65 8 << 4 | 8, /* code 0047 */
66 8 << 4 | 8, /* code 0048 */
67 3 << 4 | 4, /* code 0049 */
68 4 << 4 | 5, /* code 004A */
69 7 << 4 | 8, /* code 004B */
70 6 << 4 | 6, /* code 004C */
71 10 << 4 | 11, /* code 004D */
72 8 << 4 | 9, /* code 004E */
73 9 << 4 | 9, /* code 004F */
74 7 << 4 | 7, /* code 0050 */
75 9 << 4 | 9, /* code 0051 */
76 7 << 4 | 8, /* code 0052 */
77 6 << 4 | 6, /* code 0053 */
78 7 << 4 | 6, /* code 0054 */
79 8 << 4 | 8, /* code 0055 */
80 7 << 4 | 6, /* code 0056 */
81 10 << 4 | 11, /* code 0057 */
82 6 << 4 | 8, /* code 0058 */
83 6 << 4 | 7, /* code 0059 */
84 6 << 4 | 7, /* code 005A */
85 4 << 4 | 5, /* code 005B */
86 4 << 4 | 5, /* code 005C */
87 4 << 4 | 5, /* code 005D */
88 6 << 4 | 7, /* code 005E */
89 5 << 4 | 6, /* code 005F */
90 6 << 4 | 7, /* code 0060 */
91 6 << 4 | 7, /* code 0061 */
92 7 << 4 | 7, /* code 0062 */
93 5 << 4 | 6, /* code 0063 */
94 7 << 4 | 7, /* code 0064 */
95 6 << 4 | 7, /* code 0065 */
96 5 << 4 | 6, /* code 0066 */
97 6 << 4 | 7, /* code 0067 */
98 7 << 4 | 7, /* code 0068 */
99 3 << 4 | 4, /* code 0069 */
100 4 << 4 | 5, /* code 006A */
101 6 << 4 | 7, /* code 006B */
102 3 << 4 | 4, /* code 006C */
103 10 << 4 | 10, /* code 006D */
104 7 << 4 | 7, /* code 006E */
105 7 << 4 | 7, /* code 006F */
106 7 << 4 | 7, /* code 0070 */
107 7 << 4 | 7, /* code 0071 */
108 4 << 4 | 5, /* code 0072 */
109 5 << 4 | 6, /* code 0073 */
110 4 << 4 | 5, /* code 0074 */
111 7 << 4 | 7, /* code 0075 */
112 6 << 4 | 7, /* code 0076 */
113 9 << 4 | 10, /* code 0077 */
114 6 << 4 | 7, /* code 0078 */
115 6 << 4 | 7, /* code 0079 */
116 5 << 4 | 6, /* code 007A */
117 4 << 4 | 5, /* code 007B */
118 6 << 4 | 6, /* code 007C */
119 4 << 4 | 5, /* code 007D */
120 6 << 4 | 6, /* code 007E */
121 7 << 4 | 6, /* code 007F */
122};
123
124#if defined(HAVE_UNICODE_SUPPORT)
125// That first array will helps find the index of the unicode char:
126const unsigned int nanos_unicode_index[] = {
127 0x0000A1, 0x0000BF, 0x0000C9, 0x0000E0, 0x0000E1, 0x0000E2, 0x0000E7,
128 0x0000E8, 0x0000E9, 0x0000EA, 0x0000EB, 0x0000ED, 0x0000EE, 0x0000EF,
129 0x0000F1, 0x0000F3, 0x0000F4, 0x0000F9, 0x0000FA, 0x0000FB
130 // No need to enter FFFD, because it will be the one we'll use by default
131 // 0x00FFFD
132};
133
134// OPEN_SANS_REGULAR_11PX_UNICODE << 4 | OPEN_SANS_EXTRABOLD_11PX_UNICODE
135// Check with files (in the bagl_font_character_t array):
136// - common/bagl/src/bagl_font_open_sans_extrabold_11px_unicode.inc
137// - common/bagl/src/bagl_font_open_sans_regular_11px_unicode.inc
138const unsigned char nanos_unicode_width[] = {
139 3 << 4 | 3, // unicode 0x0000A1
140 5 << 4 | 6, // unicode 0x0000BF
141 6 << 4 | 6, // unicode 0x0000C9
142 6 << 4 | 7, // unicode 0x0000E0
143 6 << 4 | 7, // unicode 0x0000E1
144 6 << 4 | 7, // unicode 0x0000E2
145 5 << 4 | 6, // unicode 0x0000E7
146 6 << 4 | 7, // unicode 0x0000E8
147 6 << 4 | 7, // unicode 0x0000E9
148 6 << 4 | 7, // unicode 0x0000EA
149 6 << 4 | 7, // unicode 0x0000EB
150 4 << 4 | 5, // unicode 0x0000ED
151 5 << 4 | 6, // unicode 0x0000EE
152 4 << 4 | 6, // unicode 0x0000EF
153 7 << 4 | 7, // unicode 0x0000F1
154 7 << 4 | 7, // unicode 0x0000F3
155 7 << 4 | 7, // unicode 0x0000F4
156 7 << 4 | 7, // unicode 0x0000F9
157 7 << 4 | 7, // unicode 0x0000FA
158 7 << 4 | 7 // unicode 0x0000FB
159 // No need to enter FFFD, because it will be the one we'll use by default
160 // 11 << 4 |11 //unicode 0x00FFFD
161};
162
163#define DEFAULT_NANOS_UNICODE_WIDTH ((unsigned char) (11 << 4 | 11)) // For unicode FFFD
164
165#define NB_NANOS_UNICODE_CHARS (sizeof(nanos_unicode_width) / sizeof(nanos_unicode_width[0]))
166#endif // defined(HAVE_UNICODE_SUPPORT)
167
168// This function is used to retrieve the length of a string (expressed in bytes) delimited with a
169// boundary width (expressed in pixels).
170uint8_t se_get_cropped_length(const char *text,
171 uint8_t text_length,
172 uint32_t width_limit_in_pixels,
173 uint8_t text_format)
174{
175 char current_char;
176 uint8_t length;
177 uint32_t current_width_in_pixels = 0;
178
179 for (length = 0; length < text_length; length++) {
180 current_char = text[length];
181
182 if ((text_format & PAGING_FORMAT_NB) == PAGING_FORMAT_NB) {
183 // Bold.
184 current_width_in_pixels
185 += nanos_characters_width[current_char - NANOS_FIRST_CHAR] & 0x0F;
186 }
187 else {
188 // Regular.
189 current_width_in_pixels
190 += (nanos_characters_width[current_char - NANOS_FIRST_CHAR] >> 0x04) & 0x0F;
191 }
192
193 // We stop the processing when we reached the limit.
194 if (current_width_in_pixels > width_limit_in_pixels) {
195 break;
196 }
197 }
198
199 return length;
200}
201
202// This function is used to retrieve the width of a line of text.
203STATIC_IF_NOT_INDEXED unsigned int se_compute_line_width_light(const char *text,
204 uint8_t text_length,
205 uint8_t text_format)
206{
207 unsigned char ch;
208 unsigned int line_width = 0;
209#if defined(HAVE_INDEXED_STRINGS)
210 unsigned int width = 0;
211 unsigned char bold_toggle = 0;
212
213 // Bold state at the beginning of the line:
214 if ((text_format & PAGING_FORMAT_NB) == PAGING_FORMAT_NB) {
215 bold_toggle = 1;
216 }
217#endif // defined(HAVE_INDEXED_STRINGS)
218
219 // We parse the characters of the input text on all the input length.
220 while (text_length--) {
221 ch = *(const unsigned char *) text;
222#if defined(HAVE_INDEXED_STRINGS)
223 ++text;
224 width = 0;
225#endif // defined(HAVE_INDEXED_STRINGS)
226
227#if defined(HAVE_UNICODE_SUPPORT)
228 unsigned int unicode;
229
230 // Handle UTF-8 decoding (RFC3629): (https://www.ietf.org/rfc/rfc3629.txt
231 // Char. number range | UTF-8 octet sequence
232 // (hexadecimal) | (binary)
233 // --------------------+---------------------------------------------
234 // 0000 0000-0000 007F | 0xxxxxxx
235 // 0000 0080-0000 07FF | 110xxxxx 10xxxxxx
236 // 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
237 // 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
238
239 // 4 bytes UTF-8, Unicode 0x1000 to 0x1FFFF
240 if (ch >= 0xF0 && text_length >= 3) {
241 unicode = (ch & 0x07) << 18;
242 unicode |= (*((const unsigned char *) text + 0) & 0x3F) << 12;
243 unicode |= (*((const unsigned char *) text + 1) & 0x3F) << 6;
244 unicode |= (*((const unsigned char *) text + 2) & 0x3F);
245 text += 3;
246 text_length -= 3;
247
248 // 3 bytes, from 0x800 to 0xFFFF
249 }
250 else if (ch >= 0xE0 && text_length >= 2) {
251 unicode = (ch & 0x0F) << 12;
252 unicode |= (*((const unsigned char *) text + 0) & 0x3F) << 6;
253 unicode |= (*((const unsigned char *) text + 1) & 0x3F);
254 text += 2;
255 text_length -= 2;
256
257 // 2 bytes UTF-8, Unicode 0x80 to 0x7FF
258 // (0xC0 & 0xC1 are unused and can be used to store something else)
259 }
260 else if (ch >= 0xC2 && text_length >= 1) {
261 unicode = (ch & 0x1F) << 6;
262 unicode |= (*((const unsigned char *) text + 0) & 0x3F);
263 ++text;
264 text_length -= 1;
265 }
266 else {
267 unicode = 0;
268 }
269#endif // defined(HAVE_UNICODE_SUPPORT)
270
271 if (ch < NANOS_FIRST_CHAR || ch > NANOS_LAST_CHAR) {
272#if defined(HAVE_INDEXED_STRINGS)
273 // Only proceed the first line width, not the whole paragraph
274 switch (ch) {
275 case '\n':
276 case '\f':
277 return line_width;
278 case '\b': // Bold toggle: turn Bold On/Off
279 bold_toggle ^= 1;
280 continue;
281 case '\e': // Escape character => ignore it and the extra byte.
282 if (text_length >= 1) { // Take care of \e without additional byte!
283 ++text;
284 text_length -= 1;
285 }
286 continue;
287 }
288#else // defined(HAVE_INDEXED_STRINGS)
289 if (ch == '\n' || ch == '\r') {
290 break;
291 }
292#endif // defined(HAVE_INDEXED_STRINGS)
293
294#if defined(HAVE_UNICODE_SUPPORT)
295 if (unicode) {
296 unsigned int i;
297 // Find the index of the unicode character we are dealing with.
298 // For the moment, let just parse the full array, but at the end let
299 // use binary search as data are sorted by unicode value !
300 for (i = 0; i < NB_NANOS_UNICODE_CHARS; i++) {
301 if (nanos_unicode_index[i] == unicode) {
302 break;
303 }
304 }
305 // Did we find a corresponding unicode entry?
306 if (i < NB_NANOS_UNICODE_CHARS) {
307 width = nanos_unicode_width[i];
308 }
309 else {
310 // No, use FFFD character width:
311 width = DEFAULT_NANOS_UNICODE_WIDTH;
312 }
313 }
314#endif // defined(HAVE_UNICODE_SUPPORT)
315 }
316 else {
317#if defined(HAVE_INDEXED_STRINGS)
318 ch -= NANOS_FIRST_CHAR;
319 width = nanos_characters_width[ch]; // 4 MSB = regular, 4 LSB = extrabold
320#else // defined(HAVE_INDEXED_STRINGS)
321 // We retrieve the character width, and the paging format indicates whether we are
322 // processing bold characters or not.
323 if ((text_format & PAGING_FORMAT_NB) == PAGING_FORMAT_NB) {
324 // Bold.
325 line_width += nanos_characters_width[ch - NANOS_FIRST_CHAR] & 0x0F;
326 }
327 else {
328 // Regular.
329 line_width += (nanos_characters_width[ch - NANOS_FIRST_CHAR] >> 0x04) & 0x0F;
330 }
331#endif // defined(HAVE_INDEXED_STRINGS)
332 }
333#if defined(HAVE_INDEXED_STRINGS)
334 if (width) {
335 if (!bold_toggle) {
336 width >>= 4; // 4 LSB = regular, now
337 }
338 width &= 0x0F; // Keep only the 4 LSB
339 line_width += width;
340 }
341#else // defined(HAVE_INDEXED_STRINGS)
342 text++;
343#endif // defined(HAVE_INDEXED_STRINGS)
344 }
345 return line_width;
346}
347
348#endif // !HAVE_SE_SCREEN
349
350static bool is_word_delim(unsigned char c)
351{
352 // return !((c >= 'a' && c <= 'z')
353 // || (c >= 'A' && c <= 'Z')
354 // || (c >= '0' && c <= '9'));
355 return c == ' ' || c == '\n' || c == '-' || c == '_';
356}
357
358// return the number of pages to be displayed when current page to show is -1
359unsigned int ux_layout_paging_compute(const char *text_to_split,
360 unsigned int page_to_display,
361 ux_layout_paging_state_t *paging_state,
362 bagl_font_id_e font)
363{
364#ifndef HAVE_FONTS
365 UNUSED(font);
366#endif
367
368 // reset length and offset of lines
369 memset(paging_state->offsets, 0, sizeof(paging_state->offsets));
370 memset(paging_state->lengths, 0, sizeof(paging_state->lengths));
371
372 // a page has been asked, but no page exists
373 if (page_to_display >= paging_state->count && page_to_display != (unsigned int) -1) {
374 return 0;
375 }
376
377 // compute offset/length of text of each line for the current page
378 unsigned int page = 0;
379 unsigned int line = 0;
380 const char *start = (text_to_split ? STRPIC(text_to_split) : G_ux.externalText);
381 const char *start2 = start;
382 const char *end = start + strlen(start);
383 while (start < end) {
384 unsigned int len = 0;
385 unsigned int linew = 0;
386 const char *last_word_delim = start;
387 // not reached end of content
388 while (start + len < end
389 // line is not full
390 && linew <= PIXEL_PER_LINE
391 // avoid display buffer overflow for each line
392 // && len < sizeof(G_ux.string_buffer)-1
393 ) {
394 // compute new line length
395#ifdef HAVE_FONTS
396 linew = bagl_compute_line_width(font, 0, start, len + 1, BAGL_ENCODING_DEFAULT);
397#else // HAVE_FONTS
398 linew = se_compute_line_width_light(start, len + 1, G_ux.layout_paging.format);
399#endif // HAVE_FONTS
400 // if (start[len] )
401 if (linew > PIXEL_PER_LINE) {
402 // we got a full line
403 break;
404 }
405 unsigned char c = start[len];
406 if (is_word_delim(c)) {
407 last_word_delim = &start[len];
408 }
409 len++;
410 // new line, don't go further
411 if (c == '\n') {
412 break;
413 }
414 }
415
416 // if not splitting line onto a word delimiter, then cut at the previous word_delim, adjust
417 // len accordingly (and a wor delim has been found already)
418 if (start + len < end && last_word_delim != start && len) {
419 // if line split within a word
420 if ((!is_word_delim(start[len - 1]) && !is_word_delim(start[len]))) {
421 len = last_word_delim - start;
422 }
423 }
424
425 // fill up the paging structure
426 if (page_to_display != (unsigned int) -1 && page_to_display == page
427 && page_to_display < paging_state->count) {
428 paging_state->offsets[line] = start - start2;
429 paging_state->lengths[line] = len;
430
431 // won't compute all pages, we reached the one to display
432#if UX_LAYOUT_PAGING_LINE_COUNT > 1
433 if (line >= UX_LAYOUT_PAGING_LINE_COUNT - 1)
434#endif // UX_LAYOUT_PAGING_LINE_COUNT
435 {
436 // a page has been computed
437 return 1;
438 }
439 }
440
441 // prepare for next line
442 start += len;
443
444 // skip to next line/page
445 line++;
446 if (
447#if UX_LAYOUT_PAGING_LINE_COUNT > 1
448 line >= UX_LAYOUT_PAGING_LINE_COUNT &&
449#endif // UX_LAYOUT_PAGING_LINE_COUNT
450 start < end) {
451 page++;
452 line = 0;
453 }
454 }
455
456 // return total number of page detected
457 return page + 1;
458}
459
460#endif // HAVE_BAGL
461#endif // HAVE_UX_FLOW
#define BAGL_ENCODING_DEFAULT
Definition nbgl_fonts.h:96
unsigned char uint8_t
Definition usbd_conf.h:53
#define STATIC_IF_NOT_INDEXED
Definition ux_bagl.h:74
#define G_ux
Definition ux_bagl.h:345
#define STRPIC(x)