text: /**
   * @file lv_font_fmt_txt.c
   *
   */
  
  /*********************
   *      INCLUDES
   *********************/
  #include "lv_font.h"
  #include "lv_font_fmt_txt.h"
  #include "../misc/lv_assert.h"
  #include "../misc/lv_types.h"
  #include "../misc/lv_gc.h"
  #include "../misc/lv_log.h"
  #include "../misc/lv_utils.h"
  #include "../misc/lv_mem.h"
  
  /*********************
   *      DEFINES
   *********************/
  
  /**********************
   *      TYPEDEFS
   **********************/
  typedef enum {
      RLE_STATE_SINGLE = 0,
      RLE_STATE_REPEATE,
      RLE_STATE_COUNTER,
  } rle_state_t;
  
  /**********************
   *  STATIC PROTOTYPES
   **********************/
  static uint32_t get_glyph_dsc_id(const lv_font_t * font, uint32_t letter);
  static int8_t get_kern_value(const lv_font_t * font, uint32_t gid_left, uint32_t gid_right);
  static int32_t unicode_list_compare(const void * ref, const void * element);
  static int32_t kern_pair_8_compare(const void * ref, const void * element);
  static int32_t kern_pair_16_compare(const void * ref, const void * element);
  
  #if LV_USE_FONT_COMPRESSED
      static void decompress(const uint8_t * in, uint8_t * out, lv_coord_t w, lv_coord_t h, uint8_t bpp, bool prefilter);
      static inline void decompress_line(uint8_t * out, lv_coord_t w);
      static inline uint8_t get_bits(const uint8_t * in, uint32_t bit_pos, uint8_t len);
      static inline void bits_write(uint8_t * out, uint32_t bit_pos, uint8_t val, uint8_t len);
      static inline void rle_init(const uint8_t * in,  uint8_t bpp);
      static inline uint8_t rle_next(void);
  #endif /*LV_USE_FONT_COMPRESSED*/
  
  /**********************
   *  STATIC VARIABLES
   **********************/
  #if LV_USE_FONT_COMPRESSED
      static uint32_t rle_rdp;
      static const uint8_t * rle_in;
      static uint8_t rle_bpp;
      static uint8_t rle_prev_v;
      static uint8_t rle_cnt;
      static rle_state_t rle_state;
  #endif /*LV_USE_FONT_COMPRESSED*/
  
  /**********************
   * GLOBAL PROTOTYPES
   **********************/
  
  /**********************
   *      MACROS
   **********************/
  
  /**********************
   *   GLOBAL FUNCTIONS
   **********************/
  
  /**
   * Used as `get_glyph_bitmap` callback in LittelvGL's native font format if the font is uncompressed.
   * @param font pointer to font
   * @param unicode_letter an unicode letter which bitmap should be get
   * @return pointer to the bitmap or NULL if not found
   */
  const uint8_t * lv_font_get_bitmap_fmt_txt(const lv_font_t * font, uint32_t unicode_letter)
  {
      if(unicode_letter == '\t') unicode_letter = ' ';
  
      lv_font_fmt_txt_dsc_t * fdsc = (lv_font_fmt_txt_dsc_t *)font->dsc;
      uint32_t gid = get_glyph_dsc_id(font, unicode_letter);
      if(!gid) return NULL;
  
      const lv_font_fmt_txt_glyph_dsc_t * gdsc = &fdsc->glyph_dsc[gid];
  
      if(fdsc->bitmap_format == LV_FONT_FMT_TXT_PLAIN) {
          return &fdsc->glyph_bitmap[gdsc->bitmap_index];
      }
      /*Handle compressed bitmap*/
      else {
  #if LV_USE_FONT_COMPRESSED
          static size_t last_buf_size = 0;
          if(LV_GC_ROOT(_lv_font_decompr_buf) == NULL) last_buf_size = 0;
  
          uint32_t gsize = gdsc->box_w * gdsc->box_h;
          if(gsize == 0) return NULL;
  
          uint32_t buf_size = gsize;
          /*Compute memory size needed to hold decompressed glyph, rounding up*/
          switch(fdsc->bpp) {
              case 1:
                  buf_size = (gsize + 7) >> 3;
                  break;
              case 2:
                  buf_size = (gsize + 3) >> 2;
                  break;
              case 3:
                  buf_size = (gsize + 1) >> 1;
                  break;
              case 4:
                  buf_size = (gsize + 1) >> 1;
                  break;
          }
  
          if(last_buf_size < buf_size) {
              uint8_t * tmp = lv_mem_realloc(LV_GC_ROOT(_lv_font_decompr_buf), buf_size);
              LV_ASSERT_MALLOC(tmp);
              if(tmp == NULL) return NULL;
              LV_GC_ROOT(_lv_font_decompr_buf) = tmp;
              last_buf_size = buf_size;
          }
  
          bool prefilter = fdsc->bitmap_format == LV_FONT_FMT_TXT_COMPRESSED ? true : false;
          decompress(&fdsc->glyph_bitmap[gdsc->bitmap_index], LV_GC_ROOT(_lv_font_decompr_buf), gdsc->box_w, gdsc->box_h,
                     (uint8_t)fdsc->bpp, prefilter);
          return LV_GC_ROOT(_lv_font_decompr_buf);
  #else /*!LV_USE_FONT_COMPRESSED*/
          return NULL;
  #endif
      }
  
      /*If not returned earlier then the letter is not found in this font*/
      return NULL;
  }
  
  /**
   * Used as `get_glyph_dsc` callback in LittelvGL's native font format if the font is uncompressed.
   * @param font_p pointer to font
   * @param dsc_out store the result descriptor here
   * @param letter an UNICODE letter code
   * @return true: descriptor is successfully loaded into `dsc_out`.
   *         false: the letter was not found, no data is loaded to `dsc_out`
   */
  bool 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)
  {
      bool is_tab = false;
      if(unicode_letter == '\t') {
          unicode_letter = ' ';
          is_tab = true;
      }
      lv_font_fmt_txt_dsc_t * fdsc = (lv_font_fmt_txt_dsc_t *)font->dsc;
      uint32_t gid = get_glyph_dsc_id(font, unicode_letter);
      if(!gid) return false;
  
      int8_t kvalue = 0;
      if(fdsc->kern_dsc) {
          uint32_t gid_next = get_glyph_dsc_id(font, unicode_letter_next);
          if(gid_next) {
              kvalue = get_kern_value(font, gid, gid_next);
          }
      }
  
      /*Put together a glyph dsc*/
      const lv_font_fmt_txt_glyph_dsc_t * gdsc = &fdsc->glyph_dsc[gid];
  
      int32_t kv = ((int32_t)((int32_t)kvalue * fdsc->kern_scale) >> 4);
  
      uint32_t adv_w = gdsc->adv_w;
      if(is_tab) adv_w *= 2;
  
      adv_w += kv;
      adv_w  = (adv_w + (1 << 3)) >> 4;
  
      dsc_out->adv_w = adv_w;
      dsc_out->box_h = gdsc->box_h;
      dsc_out->box_w = gdsc->box_w;
      dsc_out->ofs_x = gdsc->ofs_x;
      dsc_out->ofs_y = gdsc->ofs_y;
      dsc_out->bpp   = (uint8_t)fdsc->bpp;
  
      if(is_tab) dsc_out->box_w = dsc_out->box_w * 2;
  
      return true;
  }
  
  /**
   * Free the allocated memories.
   */
  void _lv_font_clean_up_fmt_txt(void)
  {
  #if LV_USE_FONT_COMPRESSED
      if(LV_GC_ROOT(_lv_font_decompr_buf)) {
          lv_mem_free(LV_GC_ROOT(_lv_font_decompr_buf));
          LV_GC_ROOT(_lv_font_decompr_buf) = NULL;
      }
  #endif
  }
  
  /**********************
   *   STATIC FUNCTIONS
   **********************/
  
  static uint32_t get_glyph_dsc_id(const lv_font_t * font, uint32_t letter)
  {
      if(letter == '\0') return 0;
  
      lv_font_fmt_txt_dsc_t * fdsc = (lv_font_fmt_txt_dsc_t *)font->dsc;
  
      /*Check the cache first*/
      if(fdsc->cache && letter == fdsc->cache->last_letter) return fdsc->cache->last_glyph_id;
  
      uint16_t i;
      for(i = 0; i < fdsc->cmap_num; i++) {
  
          /*Relative code point*/
          uint32_t rcp = letter - fdsc->cmaps[i].range_start;
          if(rcp > fdsc->cmaps[i].range_length) continue;
          uint32_t glyph_id = 0;
          if(fdsc->cmaps[i].type == LV_FONT_FMT_TXT_CMAP_FORMAT0_TINY) {
              glyph_id = fdsc->cmaps[i].glyph_id_start + rcp;
          }
          else if(fdsc->cmaps[i].type == LV_FONT_FMT_TXT_CMAP_FORMAT0_FULL) {
              const uint8_t * gid_ofs_8 = fdsc->cmaps[i].glyph_id_ofs_list;
              glyph_id = fdsc->cmaps[i].glyph_id_start + gid_ofs_8[rcp];
          }
          else if(fdsc->cmaps[i].type == LV_FONT_FMT_TXT_CMAP_SPARSE_TINY) {
              uint16_t key = rcp;
              uint16_t * p = _lv_utils_bsearch(&key, fdsc->cmaps[i].unicode_list, fdsc->cmaps[i].list_length,
                                               sizeof(fdsc->cmaps[i].unicode_list[0]), unicode_list_compare);
  
              if(p) {
                  lv_uintptr_t ofs = p - fdsc->cmaps[i].unicode_list;
                  glyph_id = fdsc->cmaps[i].glyph_id_start + ofs;
              }
          }
          else if(fdsc->cmaps[i].type == LV_FONT_FMT_TXT_CMAP_SPARSE_FULL) {
              uint16_t key = rcp;
              uint16_t * p = _lv_utils_bsearch(&key, fdsc->cmaps[i].unicode_list, fdsc->cmaps[i].list_length,
                                               sizeof(fdsc->cmaps[i].unicode_list[0]), unicode_list_compare);
  
              if(p) {
                  lv_uintptr_t ofs = p - fdsc->cmaps[i].unicode_list;
                  const uint16_t * gid_ofs_16 = fdsc->cmaps[i].glyph_id_ofs_list;
                  glyph_id = fdsc->cmaps[i].glyph_id_start + gid_ofs_16[ofs];
              }
          }
  
          /*Update the cache*/
          if(fdsc->cache) {
              fdsc->cache->last_letter = letter;
              fdsc->cache->last_glyph_id = glyph_id;
          }
          return glyph_id;
      }
  
      if(fdsc->cache) {
          fdsc->cache->last_letter = letter;
          fdsc->cache->last_glyph_id = 0;
      }
      return 0;
  
  }
  
  static int8_t get_kern_value(const lv_font_t * font, uint32_t gid_left, uint32_t gid_right)
  {
      lv_font_fmt_txt_dsc_t * fdsc = (lv_font_fmt_txt_dsc_t *)font->dsc;
  
      int8_t value = 0;
  
      if(fdsc->kern_classes == 0) {
          /*Kern pairs*/
          const lv_font_fmt_txt_kern_pair_t * kdsc = fdsc->kern_dsc;
          if(kdsc->glyph_ids_size == 0) {
              /*Use binary search to find the kern value.
               *The pairs are ordered left_id first, then right_id secondly.*/
              const uint16_t * g_ids = kdsc->glyph_ids;
              uint16_t g_id_both = (gid_right << 8) + gid_left; /*Create one number from the ids*/
              uint16_t * kid_p = _lv_utils_bsearch(&g_id_both, g_ids, kdsc->pair_cnt, 2, kern_pair_8_compare);
  
              /*If the `g_id_both` were found get its index from the pointer*/
              if(kid_p) {
                  lv_uintptr_t ofs = kid_p - g_ids;
                  value = kdsc->values[ofs];
              }
          }
          else if(kdsc->glyph_ids_size == 1) {
              /*Use binary search to find the kern value.
               *The pairs are ordered left_id first, then right_id secondly.*/
              const uint32_t * g_ids = kdsc->glyph_ids;
              uint32_t g_id_both = (gid_right << 16) + gid_left; /*Create one number from the ids*/
              uint32_t * kid_p = _lv_utils_bsearch(&g_id_both, g_ids, kdsc->pair_cnt, 4, kern_pair_16_compare);
  
              /*If the `g_id_both` were found get its index from the pointer*/
              if(kid_p) {
                  lv_uintptr_t ofs = kid_p - g_ids;
                  value = kdsc->values[ofs];
              }
  
          }
          else {
              /*Invalid value*/
          }
      }
      else {
          /*Kern classes*/
          const lv_font_fmt_txt_kern_classes_t * kdsc = fdsc->kern_dsc;
          uint8_t left_class = kdsc->left_class_mapping[gid_left];
          uint8_t right_class = kdsc->right_class_mapping[gid_right];
  
          /*If class = 0, kerning not exist for that glyph
           *else got the value form `class_pair_values` 2D array*/
          if(left_class > 0 && right_class > 0) {
              value = kdsc->class_pair_values[(left_class - 1) * kdsc->right_class_cnt + (right_class - 1)];
          }
  
      }
      return value;
  }
  
  static int32_t kern_pair_8_compare(const void * ref, const void * element)
  {
      const uint8_t * ref8_p = ref;
      const uint8_t * element8_p = element;
  
      /*If the MSB is different it will matter. If not return the diff. of the LSB*/
      if(ref8_p[0] != element8_p[0]) return (int32_t)ref8_p[0] - element8_p[0];
      else return (int32_t) ref8_p[1] - element8_p[1];
  
  }
  
  static int32_t kern_pair_16_compare(const void * ref, const void * element)
  {
      const uint16_t * ref16_p = ref;
      const uint16_t * element16_p = element;
  
      /*If the MSB is different it will matter. If not return the diff. of the LSB*/
      if(ref16_p[0] != element16_p[0]) return (int32_t)ref16_p[0] - element16_p[0];
      else return (int32_t) ref16_p[1] - element16_p[1];
  }
  
  #if LV_USE_FONT_COMPRESSED
  /**
   * The compress a glyph's bitmap
   * @param in the compressed bitmap
   * @param out buffer to store the result
   * @param px_num number of pixels in the glyph (width * height)
   * @param bpp bit per pixel (bpp = 3 will be converted to bpp = 4)
   * @param prefilter true: the lines are XORed
   */
  static void decompress(const uint8_t * in, uint8_t * out, lv_coord_t w, lv_coord_t h, uint8_t bpp, bool prefilter)
  {
      uint32_t wrp = 0;
      uint8_t wr_size = bpp;
      if(bpp == 3) wr_size = 4;
  
      rle_init(in, bpp);
  
      uint8_t * line_buf1 = lv_mem_buf_get(w);
  
      uint8_t * line_buf2 = NULL;
  
      if(prefilter) {
          line_buf2 = lv_mem_buf_get(w);
      }
  
      decompress_line(line_buf1, w);
  
      lv_coord_t y;
      lv_coord_t x;
  
      for(x = 0; x < w; x++) {
          bits_write(out, wrp, line_buf1[x], bpp);
          wrp += wr_size;
      }
  
      for(y = 1; y < h; y++) {
          if(prefilter) {
              decompress_line(line_buf2, w);
  
              for(x = 0; x < w; x++) {
                  line_buf1[x] = line_buf2[x] ^ line_buf1[x];
                  bits_write(out, wrp, line_buf1[x], bpp);
                  wrp += wr_size;
              }
          }
          else {
              decompress_line(line_buf1, w);
  
              for(x = 0; x < w; x++) {
                  bits_write(out, wrp, line_buf1[x], bpp);
                  wrp += wr_size;
              }
          }
      }
  
      lv_mem_buf_release(line_buf1);
      lv_mem_buf_release(line_buf2);
  }
  
  /**
   * Decompress one line. Store one pixel per byte
   * @param out output buffer
   * @param w width of the line in pixel count
   */
  static inline void decompress_line(uint8_t * out, lv_coord_t w)
  {
      lv_coord_t i;
      for(i = 0; i < w; i++) {
          out[i] = rle_next();
      }
  }
  
  /**
   * Read bits from an input buffer. The read can cross byte boundary.
   * @param in the input buffer to read from.
   * @param bit_pos index of the first bit to read.
   * @param len number of bits to read (must be <= 8).
   * @return the read bits
   */
  static inline uint8_t get_bits(const uint8_t * in, uint32_t bit_pos, uint8_t len)
  {
      uint8_t bit_mask;
      switch(len) {
          case 1:
              bit_mask = 0x1;
              break;
          case 2:
              bit_mask = 0x3;
              break;
          case 3:
              bit_mask = 0x7;
              break;
          case 4:
              bit_mask = 0xF;
              break;
          case 8:
              bit_mask = 0xFF;
              break;
          default:
              bit_mask = (uint16_t)((uint16_t) 1 << len) - 1;
      }
  
      uint32_t byte_pos = bit_pos >> 3;
      bit_pos = bit_pos & 0x7;
  
      if(bit_pos + len >= 8) {
          uint16_t in16 = (in[byte_pos] << 8) + in[byte_pos + 1];
          return (in16 >> (16 - bit_pos - len)) & bit_mask;
      }
      else {
          return (in[byte_pos] >> (8 - bit_pos - len)) & bit_mask;
      }
  }
  
  /**
   * Write `val` data to `bit_pos` position of `out`. The write can NOT cross byte boundary.
   * @param out buffer where to write
   * @param bit_pos bit index to write
   * @param val value to write
   * @param len length of bits to write from `val`. (Counted from the LSB).
   * @note `len == 3` will be converted to `len = 4` and `val` will be upscaled too
   */
  static inline void bits_write(uint8_t * out, uint32_t bit_pos, uint8_t val, uint8_t len)
  {
      if(len == 3) {
          len = 4;
          switch(val) {
              case 0:
                  val = 0;
                  break;
              case 1:
                  val = 2;
                  break;
              case 2:
                  val = 4;
                  break;
              case 3:
                  val = 6;
                  break;
              case 4:
                  val = 9;
                  break;
              case 5:
                  val = 11;
                  break;
              case 6:
                  val = 13;
                  break;
              case 7:
                  val = 15;
                  break;
          }
      }
  
      uint16_t byte_pos = bit_pos >> 3;
      bit_pos = bit_pos & 0x7;
      bit_pos = 8 - bit_pos - len;
  
      uint8_t bit_mask = (uint16_t)((uint16_t) 1 << len) - 1;
      out[byte_pos] &= ((~bit_mask) << bit_pos);
      out[byte_pos] |= (val << bit_pos);
  }
  
  static inline void rle_init(const uint8_t * in,  uint8_t bpp)
  {
      rle_in = in;
      rle_bpp = bpp;
      rle_state = RLE_STATE_SINGLE;
      rle_rdp = 0;
      rle_prev_v = 0;
      rle_cnt = 0;
  }
  
  static inline uint8_t rle_next(void)
  {
      uint8_t v = 0;
      uint8_t ret = 0;
  
      if(rle_state == RLE_STATE_SINGLE) {
          ret = get_bits(rle_in, rle_rdp, rle_bpp);
          if(rle_rdp != 0 && rle_prev_v == ret) {
              rle_cnt = 0;
              rle_state = RLE_STATE_REPEATE;
          }
  
          rle_prev_v = ret;
          rle_rdp += rle_bpp;
      }
      else if(rle_state == RLE_STATE_REPEATE) {
          v = get_bits(rle_in, rle_rdp, 1);
          rle_cnt++;
          rle_rdp += 1;
          if(v == 1) {
              ret = rle_prev_v;
              if(rle_cnt == 11) {
                  rle_cnt = get_bits(rle_in, rle_rdp, 6);
                  rle_rdp += 6;
                  if(rle_cnt != 0) {
                      rle_state = RLE_STATE_COUNTER;
                  }
                  else {
                      ret = get_bits(rle_in, rle_rdp, rle_bpp);
                      rle_prev_v = ret;
                      rle_rdp += rle_bpp;
                      rle_state = RLE_STATE_SINGLE;
                  }
              }
          }
          else {
              ret = get_bits(rle_in, rle_rdp, rle_bpp);
              rle_prev_v = ret;
              rle_rdp += rle_bpp;
              rle_state = RLE_STATE_SINGLE;
          }
  
      }
      else if(rle_state == RLE_STATE_COUNTER) {
          ret = rle_prev_v;
          rle_cnt--;
          if(rle_cnt == 0) {
              ret = get_bits(rle_in, rle_rdp, rle_bpp);
              rle_prev_v = ret;
              rle_rdp += rle_bpp;
              rle_state = RLE_STATE_SINGLE;
          }
      }
  
      return ret;
  }
  #endif /*LV_USE_FONT_COMPRESSED*/
  
  /** Code Comparator.
   *
   *  Compares the value of both input arguments.
   *
   *  @param[in]  pRef        Pointer to the reference.
   *  @param[in]  pElement    Pointer to the element to compare.
   *
   *  @return Result of comparison.
   *  @retval < 0   Reference is less than element.
   *  @retval = 0   Reference is equal to element.
   *  @retval > 0   Reference is greater than element.
   *
   */
  static int32_t unicode_list_compare(const void * ref, const void * element)
  {
      return ((int32_t)(*(uint16_t *)ref)) - ((int32_t)(*(uint16_t *)element));
  }
  