1 /**
2  * @file lv_font_fmt_txt.c
3  *
4  */
5 
6 /*********************
7  *      INCLUDES
8  *********************/
9 #include "lv_font.h"
10 #include "lv_font_fmt_txt_private.h"
11 #include "../core/lv_global.h"
12 #include "../misc/lv_assert.h"
13 #include "../misc/lv_types.h"
14 #include "../misc/lv_log.h"
15 #include "../misc/lv_utils.h"
16 #include "../stdlib/lv_mem.h"
17 
18 /*********************
19  *      DEFINES
20  *********************/
21 #if LV_USE_FONT_COMPRESSED
22     #define font_rle LV_GLOBAL_DEFAULT()->font_fmt_rle
23 #endif /*LV_USE_FONT_COMPRESSED*/
24 
25 /**********************
26  *      TYPEDEFS
27  **********************/
28 typedef struct {
29     uint32_t gid_left;
30     uint32_t gid_right;
31 } kern_pair_ref_t;
32 
33 /**********************
34  *  STATIC PROTOTYPES
35  **********************/
36 static uint32_t get_glyph_dsc_id(const lv_font_t * font, uint32_t letter);
37 static int8_t get_kern_value(const lv_font_t * font, uint32_t gid_left, uint32_t gid_right);
38 static int unicode_list_compare(const void * ref, const void * element);
39 static int kern_pair_8_compare(const void * ref, const void * element);
40 static int kern_pair_16_compare(const void * ref, const void * element);
41 
42 #if LV_USE_FONT_COMPRESSED
43     static void decompress(const uint8_t * in, uint8_t * out, int32_t w, int32_t h, uint8_t bpp, bool prefilter);
44     static inline void decompress_line(uint8_t * out, int32_t w);
45     static inline uint8_t get_bits(const uint8_t * in, uint32_t bit_pos, uint8_t len);
46     static inline void rle_init(const uint8_t * in,  uint8_t bpp);
47     static inline uint8_t rle_next(void);
48 #endif /*LV_USE_FONT_COMPRESSED*/
49 
50 /**********************
51  *  STATIC VARIABLES
52  **********************/
53 
54 static const uint8_t opa4_table[16] = {0,  17, 34,  51,
55                                        68, 85, 102, 119,
56                                        136, 153, 170, 187,
57                                        204, 221, 238, 255
58                                       };
59 
60 #if LV_USE_FONT_COMPRESSED
61 static const uint8_t opa3_table[8] = {0, 36, 73, 109, 146, 182, 218, 255};
62 #endif
63 
64 static const uint8_t opa2_table[4] = {0, 85, 170, 255};
65 
66 /**********************
67  * GLOBAL PROTOTYPES
68  **********************/
69 
70 /**********************
71  *      MACROS
72  **********************/
73 
74 /**********************
75  *   GLOBAL FUNCTIONS
76  **********************/
77 
lv_font_get_bitmap_fmt_txt(lv_font_glyph_dsc_t * g_dsc,lv_draw_buf_t * draw_buf)78 const void * lv_font_get_bitmap_fmt_txt(lv_font_glyph_dsc_t * g_dsc, lv_draw_buf_t * draw_buf)
79 {
80     const lv_font_t * font = g_dsc->resolved_font;
81     uint8_t * bitmap_out = draw_buf->data;
82 
83     lv_font_fmt_txt_dsc_t * fdsc = (lv_font_fmt_txt_dsc_t *)font->dsc;
84     uint32_t gid = g_dsc->gid.index;
85     if(!gid) return NULL;
86 
87     const lv_font_fmt_txt_glyph_dsc_t * gdsc = &fdsc->glyph_dsc[gid];
88 
89     if(g_dsc->req_raw_bitmap) return &fdsc->glyph_bitmap[gdsc->bitmap_index];
90 
91     int32_t gsize = (int32_t) gdsc->box_w * gdsc->box_h;
92     if(gsize == 0) return NULL;
93 
94     bool byte_aligned = fdsc->bitmap_format == LV_FONT_FMT_PLAIN_ALIGNED;
95 
96     if(fdsc->bitmap_format == LV_FONT_FMT_TXT_PLAIN || fdsc->bitmap_format == LV_FONT_FMT_PLAIN_ALIGNED) {
97         const uint8_t * bitmap_in = &fdsc->glyph_bitmap[gdsc->bitmap_index];
98         uint8_t * bitmap_out_tmp = bitmap_out;
99         int32_t i = 0;
100         int32_t x, y;
101         uint32_t stride = lv_draw_buf_width_to_stride(gdsc->box_w, LV_COLOR_FORMAT_A8);
102         if(fdsc->bpp == 1) {
103             for(y = 0; y < gdsc->box_h; y ++) {
104                 for(x = 0; x < gdsc->box_w; x++, i++) {
105                     i = i & 0x7;
106                     if(i == 0) bitmap_out_tmp[x] = (*bitmap_in) & 0x80 ? 0xff : 0x00;
107                     else if(i == 1) bitmap_out_tmp[x] = (*bitmap_in) & 0x40 ? 0xff : 0x00;
108                     else if(i == 2) bitmap_out_tmp[x] = (*bitmap_in) & 0x20 ? 0xff : 0x00;
109                     else if(i == 3) bitmap_out_tmp[x] = (*bitmap_in) & 0x10 ? 0xff : 0x00;
110                     else if(i == 4) bitmap_out_tmp[x] = (*bitmap_in) & 0x08 ? 0xff : 0x00;
111                     else if(i == 5) bitmap_out_tmp[x] = (*bitmap_in) & 0x04 ? 0xff : 0x00;
112                     else if(i == 6) bitmap_out_tmp[x] = (*bitmap_in) & 0x02 ? 0xff : 0x00;
113                     else if(i == 7) {
114                         bitmap_out_tmp[x] = (*bitmap_in) & 0x01 ? 0xff : 0x00;
115                         bitmap_in++;
116                     }
117                 }
118                 /*Go to the next byte if stopped in the middle of a byte and
119                  *the next line is byte aligned*/
120                 if(byte_aligned && i != 0) {
121                     i = 0;
122                     bitmap_in++;
123                 }
124                 bitmap_out_tmp += stride;
125             }
126         }
127         else if(fdsc->bpp == 2) {
128             for(y = 0; y < gdsc->box_h; y ++) {
129                 for(x = 0; x < gdsc->box_w; x++, i++) {
130                     i = i & 0x3;
131                     if(i == 0) bitmap_out_tmp[x] = opa2_table[(*bitmap_in) >> 6];
132                     else if(i == 1) bitmap_out_tmp[x] = opa2_table[((*bitmap_in) >> 4) & 0x3];
133                     else if(i == 2) bitmap_out_tmp[x] = opa2_table[((*bitmap_in) >> 2) & 0x3];
134                     else if(i == 3) {
135                         bitmap_out_tmp[x] = opa2_table[((*bitmap_in) >> 0) & 0x3];
136                         bitmap_in++;
137                     }
138                 }
139 
140                 /*Go to the next byte if stopped in the middle of a byte and
141                  *the next line is byte aligned*/
142                 if(byte_aligned && i != 0) {
143                     i = 0;
144                     bitmap_in++;
145                 }
146 
147                 bitmap_out_tmp += stride;
148             }
149 
150         }
151         else if(fdsc->bpp == 4) {
152             for(y = 0; y < gdsc->box_h; y ++) {
153                 for(x = 0; x < gdsc->box_w; x++, i++) {
154                     i = i & 0x1;
155                     if(i == 0) {
156                         bitmap_out_tmp[x] = opa4_table[(*bitmap_in) >> 4];
157                     }
158                     else if(i == 1) {
159                         bitmap_out_tmp[x] = opa4_table[(*bitmap_in) & 0xF];
160                         bitmap_in++;
161                     }
162                 }
163 
164                 /*Go to the next byte if stopped in the middle of a byte and
165                  *the next line is byte aligned*/
166                 if(byte_aligned && i != 0) {
167                     i = 0;
168                     bitmap_in++;
169                 }
170 
171                 bitmap_out_tmp += stride;
172             }
173         }
174         else if(fdsc->bpp == 8) {
175             for(y = 0; y < gdsc->box_h; y ++) {
176                 for(x = 0; x < gdsc->box_w; x++, i++) {
177                     bitmap_out_tmp[x] = *bitmap_in;
178                     bitmap_in++;
179                 }
180                 bitmap_out_tmp += stride;
181             }
182         }
183         return draw_buf;
184     }
185     /*Handle compressed bitmap*/
186     else {
187 #if LV_USE_FONT_COMPRESSED
188         bool prefilter = fdsc->bitmap_format == LV_FONT_FMT_TXT_COMPRESSED;
189         decompress(&fdsc->glyph_bitmap[gdsc->bitmap_index], bitmap_out, gdsc->box_w, gdsc->box_h,
190                    (uint8_t)fdsc->bpp, prefilter);
191         return draw_buf;
192 #else /*!LV_USE_FONT_COMPRESSED*/
193         LV_LOG_WARN("Compressed fonts is used but LV_USE_FONT_COMPRESSED is not enabled in lv_conf.h");
194         return NULL;
195 #endif
196     }
197 
198     /*If not returned earlier then the letter is not found in this font*/
199     return NULL;
200 }
201 
lv_font_get_glyph_dsc_fmt_txt(const lv_font_t * font,lv_font_glyph_dsc_t * dsc_out,uint32_t unicode_letter,uint32_t unicode_letter_next)202 bool lv_font_get_glyph_dsc_fmt_txt(const lv_font_t * font, lv_font_glyph_dsc_t * dsc_out, uint32_t unicode_letter,
203                                    uint32_t unicode_letter_next)
204 {
205     /*It fixes a strange compiler optimization issue: https://github.com/lvgl/lvgl/issues/4370*/
206     bool is_tab = unicode_letter == '\t';
207     if(is_tab) {
208         unicode_letter = ' ';
209     }
210     lv_font_fmt_txt_dsc_t * fdsc = (lv_font_fmt_txt_dsc_t *)font->dsc;
211     uint32_t gid = get_glyph_dsc_id(font, unicode_letter);
212     if(!gid) return false;
213 
214     int8_t kvalue = 0;
215     if(fdsc->kern_dsc) {
216         uint32_t gid_next = get_glyph_dsc_id(font, unicode_letter_next);
217         if(gid_next) {
218             kvalue = get_kern_value(font, gid, gid_next);
219         }
220     }
221 
222     /*Put together a glyph dsc*/
223     const lv_font_fmt_txt_glyph_dsc_t * gdsc = &fdsc->glyph_dsc[gid];
224 
225     int32_t kv = ((int32_t)((int32_t)kvalue * fdsc->kern_scale) >> 4);
226 
227     uint32_t adv_w = gdsc->adv_w;
228     if(is_tab) adv_w *= 2;
229 
230     adv_w += kv;
231     adv_w  = (adv_w + (1 << 3)) >> 4;
232 
233     dsc_out->adv_w = adv_w;
234     dsc_out->box_h = gdsc->box_h;
235     dsc_out->box_w = gdsc->box_w;
236     dsc_out->ofs_x = gdsc->ofs_x;
237     dsc_out->ofs_y = gdsc->ofs_y;
238     dsc_out->format = (uint8_t)fdsc->bpp;
239     if(fdsc->bitmap_format == LV_FONT_FMT_PLAIN_ALIGNED) {
240         /*Offset in the enum to the ALIGNED values */
241         dsc_out->format += LV_FONT_GLYPH_FORMAT_A1_ALIGNED - LV_FONT_GLYPH_FORMAT_A1;
242     }
243     dsc_out->is_placeholder = false;
244     dsc_out->gid.index = gid;
245 
246     if(is_tab) dsc_out->box_w = dsc_out->box_w * 2;
247 
248     return true;
249 }
250 
251 /**********************
252  *   STATIC FUNCTIONS
253  **********************/
254 
get_glyph_dsc_id(const lv_font_t * font,uint32_t letter)255 static uint32_t get_glyph_dsc_id(const lv_font_t * font, uint32_t letter)
256 {
257     if(letter == '\0') return 0;
258 
259     lv_font_fmt_txt_dsc_t * fdsc = (lv_font_fmt_txt_dsc_t *)font->dsc;
260 
261     uint16_t i;
262     for(i = 0; i < fdsc->cmap_num; i++) {
263 
264         /*Relative code point*/
265         uint32_t rcp = letter - fdsc->cmaps[i].range_start;
266         if(rcp >= fdsc->cmaps[i].range_length) continue;
267         uint32_t glyph_id = 0;
268         if(fdsc->cmaps[i].type == LV_FONT_FMT_TXT_CMAP_FORMAT0_TINY) {
269             glyph_id = fdsc->cmaps[i].glyph_id_start + rcp;
270         }
271         else if(fdsc->cmaps[i].type == LV_FONT_FMT_TXT_CMAP_FORMAT0_FULL) {
272             const uint8_t * gid_ofs_8 = fdsc->cmaps[i].glyph_id_ofs_list;
273             /* The first character is always valid and should have offset = 0
274              * However if a character is missing it also has offset=0.
275              * So if there is a 0 not on the first position then it's a missing character */
276             if(gid_ofs_8[rcp] == 0 && letter != fdsc->cmaps[i].range_start) continue;
277             glyph_id = fdsc->cmaps[i].glyph_id_start + gid_ofs_8[rcp];
278         }
279         else if(fdsc->cmaps[i].type == LV_FONT_FMT_TXT_CMAP_SPARSE_TINY) {
280             uint16_t key = rcp;
281             uint16_t * p = lv_utils_bsearch(&key, fdsc->cmaps[i].unicode_list, fdsc->cmaps[i].list_length,
282                                             sizeof(fdsc->cmaps[i].unicode_list[0]), unicode_list_compare);
283 
284             if(p) {
285                 lv_uintptr_t ofs = p - fdsc->cmaps[i].unicode_list;
286                 glyph_id = fdsc->cmaps[i].glyph_id_start + (uint32_t) ofs;
287             }
288         }
289         else if(fdsc->cmaps[i].type == LV_FONT_FMT_TXT_CMAP_SPARSE_FULL) {
290             uint16_t key = rcp;
291             uint16_t * p = lv_utils_bsearch(&key, fdsc->cmaps[i].unicode_list, fdsc->cmaps[i].list_length,
292                                             sizeof(fdsc->cmaps[i].unicode_list[0]), unicode_list_compare);
293 
294             if(p) {
295                 lv_uintptr_t ofs = p - fdsc->cmaps[i].unicode_list;
296                 const uint16_t * gid_ofs_16 = fdsc->cmaps[i].glyph_id_ofs_list;
297                 glyph_id = fdsc->cmaps[i].glyph_id_start + gid_ofs_16[ofs];
298             }
299         }
300 
301         return glyph_id;
302     }
303 
304     return 0;
305 
306 }
307 
get_kern_value(const lv_font_t * font,uint32_t gid_left,uint32_t gid_right)308 static int8_t get_kern_value(const lv_font_t * font, uint32_t gid_left, uint32_t gid_right)
309 {
310     lv_font_fmt_txt_dsc_t * fdsc = (lv_font_fmt_txt_dsc_t *)font->dsc;
311 
312     int8_t value = 0;
313 
314     if(fdsc->kern_classes == 0) {
315         /*Kern pairs*/
316         const lv_font_fmt_txt_kern_pair_t * kdsc = fdsc->kern_dsc;
317         if(kdsc->glyph_ids_size == 0) {
318             /*Use binary search to find the kern value.
319              *The pairs are ordered left_id first, then right_id secondly.*/
320             const uint16_t * g_ids = kdsc->glyph_ids;
321             kern_pair_ref_t g_id_both = {gid_left, gid_right};
322             uint16_t * kid_p = lv_utils_bsearch(&g_id_both, g_ids, kdsc->pair_cnt, 2, kern_pair_8_compare);
323 
324             /*If the `g_id_both` were found get its index from the pointer*/
325             if(kid_p) {
326                 lv_uintptr_t ofs = kid_p - g_ids;
327                 value = kdsc->values[ofs];
328             }
329         }
330         else if(kdsc->glyph_ids_size == 1) {
331             /*Use binary search to find the kern value.
332              *The pairs are ordered left_id first, then right_id secondly.*/
333             const uint32_t * g_ids = kdsc->glyph_ids;
334             kern_pair_ref_t g_id_both = {gid_left, gid_right};
335             uint32_t * kid_p = lv_utils_bsearch(&g_id_both, g_ids, kdsc->pair_cnt, 4, kern_pair_16_compare);
336 
337             /*If the `g_id_both` were found get its index from the pointer*/
338             if(kid_p) {
339                 lv_uintptr_t ofs = kid_p - g_ids;
340                 value = kdsc->values[ofs];
341             }
342 
343         }
344         else {
345             /*Invalid value*/
346         }
347     }
348     else {
349         /*Kern classes*/
350         const lv_font_fmt_txt_kern_classes_t * kdsc = fdsc->kern_dsc;
351         uint8_t left_class = kdsc->left_class_mapping[gid_left];
352         uint8_t right_class = kdsc->right_class_mapping[gid_right];
353 
354         /*If class = 0, kerning not exist for that glyph
355          *else got the value form `class_pair_values` 2D array*/
356         if(left_class > 0 && right_class > 0) {
357             value = kdsc->class_pair_values[(left_class - 1) * kdsc->right_class_cnt + (right_class - 1)];
358         }
359 
360     }
361     return value;
362 }
363 
kern_pair_8_compare(const void * ref,const void * element)364 static int kern_pair_8_compare(const void * ref, const void * element)
365 {
366     const kern_pair_ref_t * ref8_p = ref;
367     const uint8_t * element8_p = element;
368 
369     /*If the MSB is different it will matter. If not return the diff. of the LSB*/
370     if(ref8_p->gid_left != element8_p[0]) return ref8_p->gid_left - element8_p[0];
371     else return ref8_p->gid_right - element8_p[1];
372 }
373 
kern_pair_16_compare(const void * ref,const void * element)374 static int kern_pair_16_compare(const void * ref, const void * element)
375 {
376     const kern_pair_ref_t * ref16_p = ref;
377     const uint16_t * element16_p = element;
378 
379     /*If the MSB is different it will matter. If not return the diff. of the LSB*/
380     if(ref16_p->gid_left != element16_p[0]) return ref16_p->gid_left - element16_p[0];
381     else return ref16_p->gid_right - element16_p[1];
382 }
383 
384 #if LV_USE_FONT_COMPRESSED
385 
386 /**
387  * The compress a glyph's bitmap
388  * @param in the compressed bitmap
389  * @param out buffer to store the result
390  * @param px_num number of pixels in the glyph (width * height)
391  * @param bpp bit per pixel (bpp = 3 will be converted to bpp = 4)
392  * @param prefilter true: the lines are XORed
393  */
decompress(const uint8_t * in,uint8_t * out,int32_t w,int32_t h,uint8_t bpp,bool prefilter)394 static void decompress(const uint8_t * in, uint8_t * out, int32_t w, int32_t h, uint8_t bpp, bool prefilter)
395 {
396     const lv_opa_t * opa_table;
397     switch(bpp) {
398         case 2:
399             opa_table = opa2_table;
400             break;
401         case 3:
402             opa_table = opa3_table;
403             break;
404         case 4:
405             opa_table = opa4_table;
406             break;
407         default:
408             LV_LOG_WARN("%d bpp is not handled", bpp);
409             return;
410     }
411 
412     rle_init(in, bpp);
413 
414     uint8_t * line_buf1 = lv_malloc(w);
415 
416     uint8_t * line_buf2 = NULL;
417 
418     if(prefilter) {
419         line_buf2 = lv_malloc(w);
420     }
421 
422     decompress_line(line_buf1, w);
423 
424     int32_t y;
425     int32_t x;
426     uint32_t stride = lv_draw_buf_width_to_stride(w, LV_COLOR_FORMAT_A8);
427 
428     for(x = 0; x < w; x++) {
429         out[x] = opa_table[line_buf1[x]];
430     }
431     out += stride;
432 
433     for(y = 1; y < h; y++) {
434         if(prefilter) {
435             decompress_line(line_buf2, w);
436 
437             for(x = 0; x < w; x++) {
438                 line_buf1[x] = line_buf2[x] ^ line_buf1[x];
439                 out[x] = opa_table[line_buf1[x]];
440             }
441         }
442         else {
443             decompress_line(line_buf1, w);
444 
445             for(x = 0; x < w; x++) {
446                 out[x] = opa_table[line_buf1[x]];
447             }
448         }
449         out += stride;
450     }
451 
452     lv_free(line_buf1);
453     lv_free(line_buf2);
454 }
455 
456 /**
457  * Decompress one line. Store one pixel per byte
458  * @param out output buffer
459  * @param w width of the line in pixel count
460  */
decompress_line(uint8_t * out,int32_t w)461 static inline void decompress_line(uint8_t * out, int32_t w)
462 {
463     int32_t i;
464     for(i = 0; i < w; i++) {
465         out[i] = rle_next();
466     }
467 }
468 
469 /**
470  * Read bits from an input buffer. The read can cross byte boundary.
471  * @param in the input buffer to read from.
472  * @param bit_pos index of the first bit to read.
473  * @param len number of bits to read (must be <= 8).
474  * @return the read bits
475  */
get_bits(const uint8_t * in,uint32_t bit_pos,uint8_t len)476 static inline uint8_t get_bits(const uint8_t * in, uint32_t bit_pos, uint8_t len)
477 {
478     uint8_t bit_mask;
479     switch(len) {
480         case 1:
481             bit_mask = 0x1;
482             break;
483         case 2:
484             bit_mask = 0x3;
485             break;
486         case 3:
487             bit_mask = 0x7;
488             break;
489         case 4:
490             bit_mask = 0xF;
491             break;
492         case 8:
493             bit_mask = 0xFF;
494             break;
495         default:
496             bit_mask = (uint16_t)((uint16_t) 1 << len) - 1;
497     }
498 
499     uint32_t byte_pos = bit_pos >> 3;
500     bit_pos = bit_pos & 0x7;
501 
502     if(bit_pos + len >= 8) {
503         uint16_t in16 = (in[byte_pos] << 8) + in[byte_pos + 1];
504         return (in16 >> (16 - bit_pos - len)) & bit_mask;
505     }
506     else {
507         return (in[byte_pos] >> (8 - bit_pos - len)) & bit_mask;
508     }
509 }
510 
rle_init(const uint8_t * in,uint8_t bpp)511 static inline void rle_init(const uint8_t * in,  uint8_t bpp)
512 {
513     lv_font_fmt_rle_t * rle = &font_rle;
514     rle->in = in;
515     rle->bpp = bpp;
516     rle->state = RLE_STATE_SINGLE;
517     rle->rdp = 0;
518     rle->prev_v = 0;
519     rle->count = 0;
520 }
521 
rle_next(void)522 static inline uint8_t rle_next(void)
523 {
524     uint8_t v = 0;
525     uint8_t ret = 0;
526     lv_font_fmt_rle_t * rle = &font_rle;
527 
528     if(rle->state == RLE_STATE_SINGLE) {
529         ret = get_bits(rle->in, rle->rdp, rle->bpp);
530         if(rle->rdp != 0 && rle->prev_v == ret) {
531             rle->count = 0;
532             rle->state = RLE_STATE_REPEATED;
533         }
534 
535         rle->prev_v = ret;
536         rle->rdp += rle->bpp;
537     }
538     else if(rle->state == RLE_STATE_REPEATED) {
539         v = get_bits(rle->in, rle->rdp, 1);
540         rle->count++;
541         rle->rdp += 1;
542         if(v == 1) {
543             ret = rle->prev_v;
544             if(rle->count == 11) {
545                 rle->count = get_bits(rle->in, rle->rdp, 6);
546                 rle->rdp += 6;
547                 if(rle->count != 0) {
548                     rle->state = RLE_STATE_COUNTER;
549                 }
550                 else {
551                     ret = get_bits(rle->in, rle->rdp, rle->bpp);
552                     rle->prev_v = ret;
553                     rle->rdp += rle->bpp;
554                     rle->state = RLE_STATE_SINGLE;
555                 }
556             }
557         }
558         else {
559             ret = get_bits(rle->in, rle->rdp, rle->bpp);
560             rle->prev_v = ret;
561             rle->rdp += rle->bpp;
562             rle->state = RLE_STATE_SINGLE;
563         }
564 
565     }
566     else if(rle->state == RLE_STATE_COUNTER) {
567         ret = rle->prev_v;
568         rle->count--;
569         if(rle->count == 0) {
570             ret = get_bits(rle->in, rle->rdp, rle->bpp);
571             rle->prev_v = ret;
572             rle->rdp += rle->bpp;
573             rle->state = RLE_STATE_SINGLE;
574         }
575     }
576 
577     return ret;
578 }
579 #endif /*LV_USE_FONT_COMPRESSED*/
580 
581 /** Code Comparator.
582  *
583  *  Compares the value of both input arguments.
584  *
585  *  @param[in]  pRef        Pointer to the reference.
586  *  @param[in]  pElement    Pointer to the element to compare.
587  *
588  *  @return Result of comparison.
589  *  @retval < 0   Reference is less than element.
590  *  @retval = 0   Reference is equal to element.
591  *  @retval > 0   Reference is greater than element.
592  *
593  */
unicode_list_compare(const void * ref,const void * element)594 static int unicode_list_compare(const void * ref, const void * element)
595 {
596     return (*(uint16_t *)ref) - (*(uint16_t *)element);
597 }
598