Edit

kc3-lang/kc3/libc3_window/sdl2/gl_vtext.c

Branch :

  • libc3_window/sdl2/gl_vtext.c
  • /* c3
     * Copyright 2022-2024 kmx.io <contact@kmx.io>
     *
     * Permission is hereby granted to use this software granted the above
     * copyright notice and this permission paragraph are included in all
     * copies and substantial portions of this software.
     *
     * THIS SOFTWARE IS PROVIDED "AS-IS" WITHOUT ANY GUARANTEE OF
     * PURPOSE AND PERFORMANCE. IN NO EVENT WHATSOEVER SHALL THE
     * AUTHOR BE CONSIDERED LIABLE FOR THE USE AND PERFORMANCE OF
     * THIS SOFTWARE.
     */
    #include <libc3/c3.h>
    #include "gl_vtext.h"
    
    void gl_vtext_clean (s_gl_text *text)
    {
      assert(text);
      str_clean(&text->str);
      glDeleteTextures(1, &text->texture);
    }
    
    void gl_vtext_delete (s_gl_text *text)
    {
      gl_vtext_clean(text);
      free(text);
    }
    
    s_gl_text * gl_vtext_init (s_gl_text *text, const s_gl_font *font)
    {
      s_gl_text tmp = {0};
      assert(glGetError() == GL_NO_ERROR);
      tmp.font = font;
      glGenTextures(1, &tmp.texture);
      assert(glGetError() == GL_NO_ERROR);
      *text = tmp;  
      return text;
    }
    
    s_gl_text * gl_vtext_init_1 (s_gl_text *text, const s_gl_font *font,
                                 const char *p)
    {
      s_str str;
      str_init_1(&str, NULL, p);
      return gl_vtext_init_str(text, font, &str);
    }
    
    s_gl_text * gl_vtext_init_str (s_gl_text *text, const s_gl_font *font,
                                   const s_str *str)
    {
      s_gl_text tmp = {0};
      if (! gl_vtext_init(&tmp, font))
        return NULL;
      if (! str_init_copy(&tmp.str, str))
        return NULL;
      *text = tmp;
      return text;
    }
    
    s_gl_text * gl_vtext_new (const s_gl_font *font)
    {
      s_gl_text *text;
      text = calloc(1, sizeof(s_gl_text));
      if (! text) {
        err_puts("gl_vtext_new: failed to allocate memory");
        assert(! "gl_vtext_new: failed to allocate memory");
        return NULL;
      }
      if (! gl_vtext_init(text, font)) {
        free(text);
        return NULL;
      }
      return text;
    }
    
    s_gl_text * gl_vtext_new_1 (const s_gl_font *font, const char *p)
    {
      s_gl_text *text;
      text = calloc(1, sizeof(s_gl_text));
      if (! text) {
        err_puts("gl_vtext_new_1: failed to allocate memory");
        assert(! "gl_vtext_new_1: failed to allocate memory");
        return NULL;
      }
      if (! gl_vtext_init_1(text, font, p)) {
        free(text);
        return NULL;
      }
      return text;
    }
    
    s_gl_text * gl_vtext_new_str (const s_gl_font *font, const s_str *str)
    {
      s_gl_text *text;
      text = calloc(1, sizeof(s_gl_text));
      if (! text) {
        err_puts("gl_vtext_new_1: failed to allocate memory");
        assert(! "gl_vtext_new_1: failed to allocate memory");
        return NULL;
      }
      if (! gl_vtext_init_str(text, font, str)) {
        free(text);
        return NULL;
      }
      return text;
    }
    
    bool gl_vtext_render_to_texture (s_gl_text *text)
    {
      character c;
      u8 *data;
      uw  data_w;
      uw  data_h;
      u8 *data_pixel;
      uw  data_size;
      uw  data_x;
      uw  data_y;
      FT_Face face;
      const s_gl_font *font;
      FT_GlyphSlot glyph;
      FT_UInt glyph_index;
      uw i;
      uw j;
      uw line_height;
      uw max_ascent;
      uw max_descent;
      s_str s;
      f32 scale_y;
      uw x;
      uw y;
      assert(text);
      assert(text->font);
      assert(text->texture);
      assert(glGetError() == GL_NO_ERROR);
      if (! text->str.size)
        return true;
      glBindTexture(GL_TEXTURE_2D, text->texture);
      assert(glGetError() == GL_NO_ERROR);
      font = text->font;
      face = font->ft_face;
      s = text->str;
      scale_y = face->size->metrics.y_scale / 65536.0;
      max_ascent = (u32) (face->ascender * scale_y) >> 6;
      max_descent = (u32) abs((int) (face->descender * scale_y)) >> 6;
      line_height = max_ascent + max_descent;
      data_w = 0;
      data_h = 0;
      x = 0;
      while (str_read_character_utf8(&s, &c) > 0) {
        glyph_index = FT_Get_Char_Index(face, c);
        if (FT_Load_Glyph(face, glyph_index, FT_LOAD_RENDER)) {
          err_write_1("gl_font_render_to_texture: failed to load glyph: ");
          err_inspect_character(&c);
          err_write_1("\n");
          continue;
        }
        glyph = face->glyph;
        x = glyph->metrics.horiAdvance >> 6;
        if (x > data_w)
          data_w = x;
        data_h += line_height;
      }
      data_size = data_w * data_h * 4;
      data = calloc(1, data_size);
      x = 0;
      y = 0;
      s = text->str;
      while (str_read_character_utf8(&s, &c) > 0) {
        glyph_index = FT_Get_Char_Index(face, c);
        if (FT_Load_Glyph(face, glyph_index, FT_LOAD_RENDER))
          continue;
        glyph = face->glyph;
        x = (data_w - (glyph->metrics.width >> 6)) / 2;
        i = 0;
        while (i < glyph->bitmap.rows) {
          data_y = data_h - line_height - 1 - (y + i + max_ascent - glyph->bitmap_top);
          //printf("\n");
          j = 0;
          while (j < glyph->bitmap.width) {
            data_x = x + j;
            data_pixel = data + (data_y * data_w + data_x) * 4;
            u8 value = glyph->bitmap.buffer[i * glyph->bitmap.width + j];
            data_pixel[0] = 255;
            data_pixel[1] = 255;
            data_pixel[2] = 255;
            data_pixel[3] = value;
            //printf("%s", g_gray_3_bits_utf8[value / 32]);
            j++;
          }
          i++;
        }
        y += line_height;
      }
      glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, data_w, data_h, 0, GL_RGBA,
                   GL_UNSIGNED_BYTE, data);
      assert(glGetError() == GL_NO_ERROR);
      glGenerateMipmap(GL_TEXTURE_2D);
      assert(glGetError() == GL_NO_ERROR);
      free(data);
      glBindTexture(GL_TEXTURE_2D, 0);
      text->pix_w = data_w;
      text->pix_h = data_h;
      text->pt_w = text->pix_w * text->font->point_per_pixel;
      text->pt_h = text->pix_h * text->font->point_per_pixel;
      //printf("\n");
      return true;
    }
    
    bool gl_vtext_render_to_texture_random (s_gl_text *text, uw len)
    {
      FT_UInt *glyphs;
      u8 *data;
      sw  data_w;
      sw  data_h;
      u8 *data_pixel;
      sw  data_size;
      sw  data_x;
      sw  data_y;
      FT_Face face;
      const s_gl_font *font;
      FT_GlyphSlot glyph;
      FT_UInt glyph_index;
      sw i;
      uw j;
      uw k;
      sw line_height;
      sw max_ascent;
      sw max_descent;
      f32 scale_y;
      sw x;
      sw y;
      assert(text);
      assert(text->font);
      assert(text->texture);
      assert(glGetError() == GL_NO_ERROR);
      glBindTexture(GL_TEXTURE_2D, text->texture);
      assert(glGetError() == GL_NO_ERROR);
      font = text->font;
      face = font->ft_face;
      glyphs = calloc(len, sizeof(FT_UInt));
      if (! glyphs) {
        err_puts("gl_vtext_render_to_texture_random:"
                 " failed to allocate memory (glyphs)");
        assert(! "gl_vtext_render_to_texture_random:"
                 " failed to allocate memory (glyphs)");
        return false;
      }
      for (i = 0; i < (sw) len; i++) {
        glyph = NULL;
        do {
          u32_random_uniform(glyphs + i, face->num_glyphs - 2);
          glyphs[i]++;
          if (FT_Load_Glyph(face, glyphs[i], FT_LOAD_RENDER))
            continue;
          glyph = face->glyph;
          //printf("width %ld\n", glyph->metrics.width >> 6);
        } while (! glyph || ! (glyph->metrics.width >> 6));
      }
      scale_y = face->size->metrics.y_scale / 65536.0;
      max_ascent = (u32) (face->ascender * scale_y) >> 6;
      max_descent = (u32) abs((int) (face->descender * scale_y)) >> 6;
      line_height = max_ascent + max_descent;
      data_w = 0;
      data_h = 0;
      x = 0;
      for (i = 0; i < (sw) len; i++) {
        glyph_index = glyphs[i];
        if (FT_Load_Glyph(face, glyph_index, FT_LOAD_RENDER)) {
          err_write_1("gl_vtext_render_to_texture_random:"
                      " failed to load glyph: ");
          err_inspect_u32(&glyph_index);
          err_write_1("\n");
          continue;
        }
        glyph = face->glyph;
        x = glyph->metrics.horiAdvance >> 6;
        if (x > data_w)
          data_w = x;
        data_h += line_height;
      }
      data_w++;
      data_h += line_height * 2;
      data_size = data_w * data_h * 4;
      data = calloc(1, data_size);
      if (! data) {
        err_puts("gl_vtext_render_to_texture_random:"
                 " failed to allocate memory (data)");
        assert(! "gl_vtext_render_to_texture_random:"
                 " failed to allocate memory (data)");
        free(glyphs);
        return false;
      }
      x = 0;
      y = 0;
      for (i = 0; i < (sw) len; i++) {
        glyph_index = glyphs[i];
        if (FT_Load_Glyph(face, glyph_index, FT_LOAD_RENDER))
          continue;
        glyph = face->glyph;
        x = (data_w - (glyph->metrics.width >> 6)) / 2;
        j = 0;
        while (j < glyph->bitmap.rows) {
          data_y = data_h - line_height - 1 -
            ((sw) y + j + max_ascent - glyph->bitmap_top);
          //printf("\n");
          k = 0;
          while (k < glyph->bitmap.width) {
            data_x = x + k;
            data_pixel = data + (data_y * data_w + data_x) * 4;
            u8 value = glyph->bitmap.buffer[j * glyph->bitmap.width + k];
            data_pixel[0] = 255;
            data_pixel[1] = 255;
            data_pixel[2] = 255;
            data_pixel[3] = value;
            //printf("%s", g_gray_3_bits_utf8[value / 32]);
            k++;
          }
          j++;
        }
        y += line_height;
      }
      free(glyphs);
      glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, data_w, data_h, 0, GL_RGBA,
                   GL_UNSIGNED_BYTE, data);
      assert(glGetError() == GL_NO_ERROR);
      glGenerateMipmap(GL_TEXTURE_2D);
      assert(glGetError() == GL_NO_ERROR);
      free(data);
      glBindTexture(GL_TEXTURE_2D, 0);
      text->pix_w = data_w;
      text->pix_h = data_h;
      text->pt_w = text->pix_w * text->font->point_per_pixel;
      text->pt_h = text->pix_h * text->font->point_per_pixel;
      //printf("\n");
      return true;
    }
    
    bool gl_vtext_set_font (s_gl_text *text, const s_gl_font *font)
    {
      assert(text);
      assert(font);
      if (text->font != font) {
        text->font = font;
        return true;
      }
      return false;
    }
    
    bool gl_vtext_set_text (s_gl_text *text, const s_str *str)
    {
      assert(text);
      assert(str);
      if (compare_str(&text->str, str)) {
        str_clean(&text->str);
        str_init_copy(&text->str, str);
        return true;
      }
      return false;
    }
    
    bool gl_vtext_set_text_1 (s_gl_text *text, const char *p)
    {
      s_str str;
      assert(text);
      assert(p);
      str_init_1(&str, NULL, p);
      return gl_vtext_set_text(text, &str);
    }
    
    bool gl_vtext_set_text_buf (s_gl_text *text, s_buf *buf)
    {
      bool result;
      s_str str;
      assert(text);
      assert(buf);
      buf_peek_to_str(buf, &str);
      result = gl_vtext_set_text(text, &str);
      str_clean(&str);
      return result;
    }
    
    bool gl_vtext_update (s_gl_text *text)
    {
      return gl_vtext_render_to_texture(text);
    }
    
    bool gl_vtext_update_1 (s_gl_text *text, const char *p)
    {
      if (gl_vtext_set_text_1(text, p)) {
        gl_vtext_update(text);
        return true;
      }
      return false;
    }
    
    bool gl_vtext_update_buf (s_gl_text *text, s_buf *buf)
    {
      if (gl_vtext_set_text_buf(text, buf)) {
        gl_vtext_update(text);
        return true;
      }
      return false;
    }