Edit

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

Branch :

  • Show log

    Commit

  • Author : thodg Thomas de Grivel
    Date : 2024-03-24 14:53:08
    Hash : 75270fe7
    Message : move libc3/window to libc3_window, shorter build messages and build log for libc3 (see configure)

  • libc3_window/sdl2/gl_text.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_text.h"
    #include "window_sdl2.h"
    
    void gl_text_clean (s_gl_text *text)
    {
      str_clean(&text->str);
      glDeleteTextures(1, &text->texture);
    }
    
    s_gl_text * gl_text_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_text_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_text_init_str(text, font, &str);
    }
    
    s_gl_text * gl_text_init_str (s_gl_text *text, const s_gl_font *font,
                                  const s_str *str)
    {
      s_gl_text tmp = {0};
      if (! gl_text_init(&tmp, font))
        return NULL;
      if (! str_init_copy(&tmp.str, str))
        return NULL;
      *text = tmp;
      return text;
    }
    
    bool gl_text_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_Vector delta;
      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;
      FT_UInt prev_glyph_index;
      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 = line_height;
      x = 0;
      prev_glyph_index = 0;
      while (str_read_character_utf8(&s, &c) > 0) {
        if (c == '\n') {
          if (x > data_w)
            data_w = x;
          x = 0;
          data_h += line_height;
          prev_glyph_index = 0;
          continue;
        }
        glyph_index = FT_Get_Char_Index(face, c);
        if (prev_glyph_index && glyph_index) {
          FT_Get_Kerning(face, prev_glyph_index, glyph_index,
                         FT_KERNING_DEFAULT, &delta);
          x += delta.x >> 6;
        }
        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;
        prev_glyph_index = glyph_index;
      }
      if (x > data_w)
        data_w = x;
      data_size = data_w * data_h * 4;
      data = calloc(1, data_size);
      x = 0;
      y = 0;
      prev_glyph_index = 0;
      s = text->str;
      while (str_read_character_utf8(&s, &c) > 0) {
        if (c == '\n') {
          x = 0;
          y += line_height;
          prev_glyph_index = 0;
          continue;
        }
        glyph_index = FT_Get_Char_Index(face, c);
        if (prev_glyph_index && glyph_index) {
          FT_Vector delta;
          FT_Get_Kerning(face, prev_glyph_index, glyph_index,
                         FT_KERNING_DEFAULT, &delta);
          x += delta.x >> 6;
        }
        if (FT_Load_Glyph(face, glyph_index, FT_LOAD_RENDER))
          continue;
        glyph = face->glyph;
        i = 0;
        while (i < glyph->bitmap.rows) {
          data_y = data_h - 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++;
        }
        x += glyph->metrics.horiAdvance >> 6;
        prev_glyph_index = glyph_index;
      }
      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_text_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_text_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_text_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_text_set_text(text, &str);
    }
    
    bool gl_text_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_text_set_text(text, &str);
      str_clean(&str);
      return result;
    }
    
    bool gl_text_update (s_gl_text *text)
    {
      return gl_text_render_to_texture(text);
    }
    
    bool gl_text_update_1 (s_gl_text *text, const char *p)
    {
      if (gl_text_set_text_1(text, p)) {
        gl_text_update(text);
        return true;
      }
      return false;
    }
    
    bool gl_text_update_buf (s_gl_text *text, s_buf *buf)
    {
      if (gl_text_set_text_buf(text, buf)) {
        gl_text_update(text);
        return true;
      }
      return false;
    }