Ejemplo n.º 1
0
static int wiiu_font_get_message_width(void* data, const char* msg,
                                      unsigned msg_len, float scale)
{
   wiiu_font_t* font = (wiiu_font_t*)data;

   unsigned i;
   int delta_x = 0;

   if (!font)
      return 0;

   for (i = 0; i < msg_len; i++)
   {
      const char* msg_tmp            = &msg[i];
      unsigned code                  = utf8_walk(&msg_tmp);
      unsigned skip                  = msg_tmp - &msg[i];

      if (skip > 1)
         i += skip - 1;

      const struct font_glyph* glyph =
         font->font_driver->get_glyph(font->font_data, code);

      if (!glyph) /* Do something smarter here ... */
         glyph = font->font_driver->get_glyph(font->font_data, '?');

      if (!glyph)
         continue;

      delta_x += glyph->advance_x;
   }

   return delta_x * scale;
}
Ejemplo n.º 2
0
static int gl_get_message_width(void *data, const char *msg,
      unsigned msg_len, float scale)
{
   gl_raster_t *font   = (gl_raster_t*)data;
   const char* msg_end = msg + msg_len;
   int delta_x         = 0;

   if (     !font
         || !font->font_driver
         || !font->font_driver->get_glyph
         || !font->font_data )
      return 0;

   while (msg < msg_end)
   {
      unsigned code                  = utf8_walk(&msg);
      const struct font_glyph *glyph = font->font_driver->get_glyph(
            font->font_data, code);

      if (!glyph) /* Do something smarter here ... */
         glyph = font->font_driver->get_glyph(font->font_data, '?');
      if (!glyph)
         continue;

      delta_x += glyph->advance_x;
   }

   return delta_x * scale;
}
Ejemplo n.º 3
0
static void osk_update_last_codepoint(const char *word)
{
   const char *letter = word;
   const char *pos = letter;

   if (letter[0] == 0)
   {
      osk_last_codepoint = 0;
      osk_last_codepoint_len = 0;
      return;
   }

   for (;;)
   {
      unsigned codepoint = utf8_walk(&letter);
      unsigned len = letter - pos;

      if (letter[0] == 0)
      {
         osk_last_codepoint = codepoint;
         osk_last_codepoint_len = len;
         break;
      }

      pos = letter;
   }
}
Ejemplo n.º 4
0
static void wiiu_font_render_line(
      video_frame_info_t *video_info,
      wiiu_font_t* font, const char* msg, unsigned msg_len,
      float scale, const unsigned int color, float pos_x,
      float pos_y, unsigned text_align)
{
   unsigned i;
   wiiu_video_t* wiiu = (wiiu_video_t*)video_info->userdata;
   unsigned width   = video_info->width;
   unsigned height  = video_info->height;
   int x            = roundf(pos_x * width);
   int y            = roundf((1.0 - pos_y) * height);

   if(  !wiiu ||
         wiiu->vertex_cache.current + (msg_len * 4) > wiiu->vertex_cache.size)
      return;

   switch (text_align)
   {
      case TEXT_ALIGN_RIGHT:
         x -= wiiu_font_get_message_width(font, msg, msg_len, scale);
         break;

      case TEXT_ALIGN_CENTER:
         x -= wiiu_font_get_message_width(font, msg, msg_len, scale) / 2;
         break;
   }

   sprite_vertex_t* v = wiiu->vertex_cache.v + wiiu->vertex_cache.current;

   for (i = 0; i < msg_len; i++)
   {
      const char* msg_tmp            = &msg[i];
      unsigned code                  = utf8_walk(&msg_tmp);
      unsigned skip                  = msg_tmp - &msg[i];

      if (skip > 1)
         i += skip - 1;

      const struct font_glyph* glyph =
         font->font_driver->get_glyph(font->font_data, code);

      if (!glyph) /* Do something smarter here ... */
         glyph = font->font_driver->get_glyph(font->font_data, '?');

      if (!glyph)
         continue;

      v->pos.x = x + glyph->draw_offset_x * scale;
      v->pos.y = y + glyph->draw_offset_y * scale;
      v->pos.width = glyph->width * scale;
      v->pos.height = glyph->height * scale;

      v->coord.u = glyph->atlas_offset_x;
      v->coord.v = glyph->atlas_offset_y;
      v->coord.width = glyph->width;
      v->coord.height = glyph->height;

      v->color = color;

      v++;

      x += glyph->advance_x * scale;
      y += glyph->advance_y * scale;
   }

   int count = v - wiiu->vertex_cache.v - wiiu->vertex_cache.current;

   if (!count)
      return;

   GX2Invalidate(GX2_INVALIDATE_MODE_CPU_ATTRIBUTE_BUFFER, wiiu->vertex_cache.v + wiiu->vertex_cache.current, count * sizeof(wiiu->vertex_cache.v));

   if(font->atlas->dirty)
   {
      for (i = 0; (i < font->atlas->height) && (i < font->texture.surface.height); i++)
         memcpy(font->texture.surface.image + (i * font->texture.surface.pitch),
                font->atlas->buffer + (i * font->atlas->width), font->atlas->width);

      GX2Invalidate(GX2_INVALIDATE_MODE_CPU_TEXTURE, font->texture.surface.image,
                    font->texture.surface.imageSize);
      font->atlas->dirty = false;
   }

   GX2SetPixelTexture(&font->texture, sprite_shader.ps.samplerVars[0].location);
   GX2SetVertexUniformBlock(sprite_shader.vs.uniformBlocks[1].offset, sprite_shader.vs.uniformBlocks[1].size, font->ubo_tex);

   GX2DrawEx(GX2_PRIMITIVE_MODE_POINTS, count, wiiu->vertex_cache.current, 1);

   GX2SetVertexUniformBlock(sprite_shader.vs.uniformBlocks[1].offset, sprite_shader.vs.uniformBlocks[1].size, wiiu->ubo_tex);

   wiiu->vertex_cache.current = v - wiiu->vertex_cache.v;
}
Ejemplo n.º 5
0
static void gl_raster_font_render_line(
      gl_raster_t *font, const char *msg, unsigned msg_len,
      GLfloat scale, const GLfloat color[4], GLfloat pos_x,
      GLfloat pos_y, unsigned text_align)
{
   int x, y, delta_x, delta_y;
   unsigned i;
   struct video_coords coords;
   float inv_tex_size_x, inv_tex_size_y, inv_win_width, inv_win_height;
   GLfloat font_tex_coords[2 * 6 * MAX_MSG_LEN_CHUNK];
   GLfloat font_vertex[2 * 6 * MAX_MSG_LEN_CHUNK];
   GLfloat font_color[4 * 6 * MAX_MSG_LEN_CHUNK];
   GLfloat font_lut_tex_coord[2 * 6 * MAX_MSG_LEN_CHUNK];
   gl_t      *gl       = font ? font->gl : NULL;
   const char* msg_end = msg + msg_len;

   if (!gl)
      return;

   x              = roundf(pos_x * gl->vp.width);
   y              = roundf(pos_y * gl->vp.height);
   delta_x        = 0;
   delta_y        = 0;

   switch (text_align)
   {
      case TEXT_ALIGN_RIGHT:
         x -= gl_get_message_width(font, msg, msg_len, scale);
         break;
      case TEXT_ALIGN_CENTER:
         x -= gl_get_message_width(font, msg, msg_len, scale) / 2.0;
         break;
   }

   inv_tex_size_x = 1.0f / font->tex_width;
   inv_tex_size_y = 1.0f / font->tex_height;
   inv_win_width  = 1.0f / font->gl->vp.width;
   inv_win_height = 1.0f / font->gl->vp.height;

   while (msg < msg_end)
   {
      i = 0;
      while ((i < MAX_MSG_LEN_CHUNK) && (msg < msg_end))
      {         
         int off_x, off_y, tex_x, tex_y, width, height;
         unsigned                  code = utf8_walk(&msg);
         const struct font_glyph *glyph = font->font_driver->get_glyph(
               font->font_data, code);

         if (!glyph) /* Do something smarter here ... */
            glyph = font->font_driver->get_glyph(font->font_data, '?');

         if (!glyph)
            continue;

         off_x  = glyph->draw_offset_x;
         off_y  = glyph->draw_offset_y;
         tex_x  = glyph->atlas_offset_x;
         tex_y  = glyph->atlas_offset_y;
         width  = glyph->width;
         height = glyph->height;

         gl_raster_font_emit(0, 0, 1); /* Bottom-left */
         gl_raster_font_emit(1, 1, 1); /* Bottom-right */
         gl_raster_font_emit(2, 0, 0); /* Top-left */

         gl_raster_font_emit(3, 1, 0); /* Top-right */
         gl_raster_font_emit(4, 0, 0); /* Top-left */
         gl_raster_font_emit(5, 1, 1); /* Bottom-right */

         i++;

         delta_x += glyph->advance_x;
         delta_y -= glyph->advance_y;
      }

      coords.tex_coord     = font_tex_coords;
      coords.vertex        = font_vertex;
      coords.color         = font_color;
      coords.vertices      = i * 6;
      coords.lut_tex_coord = font_lut_tex_coord;

      if (font->block)
         video_coord_array_append(&font->block->carr, &coords, coords.vertices);
      else
         gl_raster_font_draw_vertices(font, &coords);
   }
}
Ejemplo n.º 6
0
static void d3d12_font_render_line(
      video_frame_info_t* video_info,
      d3d12_font_t*       font,
      const char*         msg,
      unsigned            msg_len,
      float               scale,
      const unsigned int  color,
      float               pos_x,
      float               pos_y,
      unsigned            text_align)
{
   unsigned        i, count;
   void*           mapped_vbo = NULL;
   d3d12_sprite_t* v          = NULL;
   d3d12_sprite_t* vbo_start  = NULL;
   d3d12_video_t*  d3d12      = (d3d12_video_t*)video_info->userdata;
   unsigned        width      = video_info->width;
   unsigned        height     = video_info->height;
   int             x          = roundf(pos_x * width);
   int             y          = roundf((1.0 - pos_y) * height);
   D3D12_RANGE     range      = { 0, 0 };

   if (  !d3d12                  || 
         !d3d12->sprites.enabled || 
         msg_len > (unsigned)d3d12->sprites.capacity)
      return;

   if (d3d12->sprites.offset + msg_len > (unsigned)d3d12->sprites.capacity)
      d3d12->sprites.offset = 0;

   switch (text_align)
   {
      case TEXT_ALIGN_RIGHT:
         x -= d3d12_font_get_message_width(font, msg, msg_len, scale);
         break;

      case TEXT_ALIGN_CENTER:
         x -= d3d12_font_get_message_width(font, msg, msg_len, scale) / 2;
         break;
   }

   D3D12Map(d3d12->sprites.vbo, 0, &range, (void**)&vbo_start);

   v           = vbo_start + d3d12->sprites.offset;
   range.Begin = (uintptr_t)v - (uintptr_t)vbo_start;

   for (i = 0; i < msg_len; i++)
   {
      const struct font_glyph* glyph;
      const char*              msg_tmp = &msg[i];
      unsigned                 code    = utf8_walk(&msg_tmp);
      unsigned                 skip    = msg_tmp - &msg[i];

      if (skip > 1)
         i += skip - 1;

      glyph = font->font_driver->get_glyph(font->font_data, code);

      if (!glyph) /* Do something smarter here ... */
         glyph = font->font_driver->get_glyph(font->font_data, '?');

      if (!glyph)
         continue;

      v->pos.x = (x + glyph->draw_offset_x) * scale / (float)d3d12->chain.viewport.Width;
      v->pos.y = (y + glyph->draw_offset_y) * scale / (float)d3d12->chain.viewport.Height;
      v->pos.w = glyph->width * scale  / (float)d3d12->chain.viewport.Width;
      v->pos.h = glyph->height * scale / (float)d3d12->chain.viewport.Height;

      v->coords.u = glyph->atlas_offset_x / (float)font->texture.desc.Width;
      v->coords.v = glyph->atlas_offset_y / (float)font->texture.desc.Height;
      v->coords.w = glyph->width          / (float)font->texture.desc.Width;
      v->coords.h = glyph->height         / (float)font->texture.desc.Height;

      v->params.scaling  = 1;
      v->params.rotation = 0;

      v->colors[0] = color;
      v->colors[1] = color;
      v->colors[2] = color;
      v->colors[3] = color;

      v++;

      x += glyph->advance_x * scale;
      y += glyph->advance_y * scale;
   }

   range.End = (uintptr_t)v - (uintptr_t)vbo_start;
   D3D12Unmap(d3d12->sprites.vbo, 0, &range);

   count = v - vbo_start - d3d12->sprites.offset;

   if (!count)
      return;

   if (font->atlas->dirty)
   {
      d3d12_update_texture(
            font->atlas->width, font->atlas->height,
            font->atlas->width, DXGI_FORMAT_A8_UNORM,
            font->atlas->buffer, &font->texture);
      font->atlas->dirty = false;
   }

   if(font->texture.dirty)
      d3d12_upload_texture(d3d12->queue.cmd, &font->texture,
            video_info->userdata);

   D3D12SetPipelineState(d3d12->queue.cmd, d3d12->sprites.pipe_font);
   d3d12_set_texture_and_sampler(d3d12->queue.cmd, &font->texture);
   D3D12DrawInstanced(d3d12->queue.cmd, count, 1, d3d12->sprites.offset, 0);

   D3D12SetPipelineState(d3d12->queue.cmd, d3d12->sprites.pipe);

   d3d12->sprites.offset += count;
}
Ejemplo n.º 7
0
static void switch_font_render_line(
    video_frame_info_t *video_info,
    switch_font_t *font, const char *msg, unsigned msg_len,
    float scale, const unsigned int color, float pos_x,
    float pos_y, unsigned text_align)
{
      int delta_x = 0;
      int delta_y = 0;

      unsigned fbWidth = 0;
      unsigned fbHeight = 0;

      uint32_t *out_buffer = (uint32_t *)gfxGetFramebuffer(&fbWidth, &fbHeight);
      if (out_buffer)
      {
            int x = roundf(pos_x * fbWidth);
            int y = roundf((1.0f - pos_y) * fbHeight);

            switch (text_align)
            {
            case TEXT_ALIGN_RIGHT:
                  x -= switch_font_get_message_width(font, msg, msg_len, scale);
                  break;
            case TEXT_ALIGN_CENTER:
                  x -= switch_font_get_message_width(font, msg, msg_len, scale) / 2;
                  break;
            }

            for (int i = 0; i < msg_len; i++)
            {
                  int off_x, off_y, tex_x, tex_y, width, height;
                  const char *msg_tmp = &msg[i];
                  unsigned code = utf8_walk(&msg_tmp);
                  unsigned skip = msg_tmp - &msg[i];

                  if (skip > 1)
                        i += skip - 1;

                  const struct font_glyph *glyph =
                      font->font_driver->get_glyph(font->font_data, code);

                  if (!glyph) /* Do something smarter here ... */
                        glyph = font->font_driver->get_glyph(font->font_data, '?');

                  if (!glyph)
                        continue;

                  off_x = x + glyph->draw_offset_x + delta_x;
                  off_y = y + glyph->draw_offset_y + delta_y;
                  width = glyph->width;
                  height = glyph->height;

                  tex_x = glyph->atlas_offset_x;
                  tex_y = glyph->atlas_offset_y;

                  for (int y = tex_y; y < tex_y + height; y++)
                  {
                        uint8_t *row = &font->atlas->buffer[y * font->atlas->width];
                        for (int x = tex_x; x < tex_x + width; x++)
                        {
                            if (!row[x])
                                continue;
                              int x1 = off_x + (x - tex_x);
                              int y1 = off_y + (y - tex_y);
                              if (x1 < fbWidth && y1 < fbHeight)
                                  out_buffer[gfxGetFramebufferDisplayOffset(x1, y1)] = color;
                        }
                  }

                  delta_x += glyph->advance_x;
                  delta_y += glyph->advance_y;
            }
      }
}
Ejemplo n.º 8
0
static void vita2d_font_render_line(
      vita_font_t *font, const char *msg, unsigned msg_len,
      float scale, const unsigned int color, float pos_x,
      float pos_y, unsigned text_align)
{
   int x, y, delta_x, delta_y;
	 unsigned width, height;
   unsigned i;

	 video_driver_get_size(&width, &height);

   x       = roundf(pos_x * width);
   y       = roundf((1.0f - pos_y) * height);
   delta_x = 0;
   delta_y = 0;


   switch (text_align)
   {
      case TEXT_ALIGN_RIGHT:
         x -= vita2d_font_get_message_width(font, msg, msg_len, scale);
         break;
      case TEXT_ALIGN_CENTER:
         x -= vita2d_font_get_message_width(font, msg, msg_len, scale) / 2;
         break;
   }

   for (i = 0; i < msg_len; i++)
   {
      int off_x, off_y, tex_x, tex_y, width, height;
      const char *msg_tmp            = &msg[i];
      unsigned code                  = utf8_walk(&msg_tmp);
      unsigned skip                  = msg_tmp - &msg[i];

      if (skip > 1)
         i += skip - 1;
         
      const struct font_glyph *glyph =
         font->font_driver->get_glyph(font->font_data, code);

      if (!glyph) /* Do something smarter here ... */
         glyph = font->font_driver->get_glyph(font->font_data, '?');
      if (!glyph)
         continue;

      off_x  = glyph->draw_offset_x;
      off_y  = glyph->draw_offset_y;
      tex_x  = glyph->atlas_offset_x;
      tex_y  = glyph->atlas_offset_y;
      width  = glyph->width;
      height = glyph->height;

			vita2d_draw_texture_tint_part_scale(font->texture,
				x + off_x + delta_x * scale,
				y + off_y + delta_y * scale,
				tex_x, tex_y, width, height,
				scale,
				scale,
				color);

      delta_x += glyph->advance_x;
      delta_y += glyph->advance_y;
   }
}
Ejemplo n.º 9
0
static void vulkan_raster_font_render_line(
      vulkan_raster_t *font, const char *msg, unsigned msg_len,
      float scale, const float color[4], float pos_x,
      float pos_y, unsigned text_align)
{
   struct vk_color vk_color;
   vk_t *vk             = font->vk;
   const char* msg_end  = msg + msg_len;
   int x                = roundf(pos_x * vk->vp.width);
   int y                = roundf((1.0f - pos_y) * vk->vp.height);
   int delta_x          = 0;
   int delta_y          = 0;
   float inv_tex_size_x = 1.0f / font->texture.width;
   float inv_tex_size_y = 1.0f / font->texture.height;
   float inv_win_width  = 1.0f / font->vk->vp.width;
   float inv_win_height = 1.0f / font->vk->vp.height;

   vk_color.r           = color[0];
   vk_color.g           = color[1];
   vk_color.b           = color[2];
   vk_color.a           = color[3];

   switch (text_align)
   {
      case TEXT_ALIGN_RIGHT:
         x -= vulkan_get_message_width(font, msg, msg_len, scale);
         break;
      case TEXT_ALIGN_CENTER:
         x -= vulkan_get_message_width(font, msg, msg_len, scale) / 2;
         break;
   }

   while (msg < msg_end)
   {
      int off_x, off_y, tex_x, tex_y, width, height;
      unsigned code                  = utf8_walk(&msg);
      const struct font_glyph *glyph =
         font->font_driver->get_glyph(font->font_data, code);

      if (!glyph) /* Do something smarter here ... */
         glyph = font->font_driver->get_glyph(font->font_data, '?');
      if (!glyph)
         continue;

      vulkan_raster_font_update_glyph(font, glyph);

      off_x  = glyph->draw_offset_x;
      off_y  = glyph->draw_offset_y;
      tex_x  = glyph->atlas_offset_x;
      tex_y  = glyph->atlas_offset_y;
      width  = glyph->width;
      height = glyph->height;

      vulkan_write_quad_vbo(font->pv + font->vertices,
            (x + off_x + delta_x * scale) * inv_win_width,
            (y + off_y + delta_y * scale) * inv_win_height,
            width * scale * inv_win_width,
            height * scale * inv_win_height,
            tex_x * inv_tex_size_x,
            tex_y * inv_tex_size_y,
            width * inv_tex_size_x,
            height * inv_tex_size_y,
            &vk_color);

      font->vertices += 6;

      delta_x        += glyph->advance_x;
      delta_y        += glyph->advance_y;
   }
}
Ejemplo n.º 10
0
static void d3d11_font_render_line(
      video_frame_info_t* video_info,
      d3d11_font_t*       font,
      const char*         msg,
      unsigned            msg_len,
      float               scale,
      const unsigned int  color,
      float               pos_x,
      float               pos_y,
      unsigned            text_align)
{
   unsigned                 i, count;
   D3D11_MAPPED_SUBRESOURCE mapped_vbo;
   d3d11_sprite_t*          v;
   d3d11_video_t*           d3d11  = (d3d11_video_t*)video_driver_get_ptr(false);
   unsigned                 width  = video_info->width;
   unsigned                 height = video_info->height;
   int                      x      = roundf(pos_x * width);
   int                      y      = roundf((1.0 - pos_y) * height);

   if (!d3d11->sprites.enabled || msg_len > (unsigned)d3d11->sprites.capacity)
      return;

   if (d3d11->sprites.offset + msg_len > (unsigned)d3d11->sprites.capacity)
      d3d11->sprites.offset = 0;

   switch (text_align)
   {
      case TEXT_ALIGN_RIGHT:
         x -= d3d11_font_get_message_width(font, msg, msg_len, scale);
         break;

      case TEXT_ALIGN_CENTER:
         x -= d3d11_font_get_message_width(font, msg, msg_len, scale) / 2;
         break;
   }

   D3D11MapBuffer(d3d11->context, d3d11->sprites.vbo, 0, D3D11_MAP_WRITE_NO_OVERWRITE, 0, &mapped_vbo);
   v = (d3d11_sprite_t*)mapped_vbo.pData + d3d11->sprites.offset;

   for (i = 0; i < msg_len; i++)
   {
      const struct font_glyph* glyph;
      const char*              msg_tmp = &msg[i];
      unsigned                 code    = utf8_walk(&msg_tmp);
      unsigned                 skip    = msg_tmp - &msg[i];

      if (skip > 1)
         i += skip - 1;

      glyph = font->font_driver->get_glyph(font->font_data, code);

      if (!glyph) /* Do something smarter here ... */
         glyph = font->font_driver->get_glyph(font->font_data, '?');

      if (!glyph)
         continue;

      v->pos.x = (x + glyph->draw_offset_x) * scale / (float)d3d11->viewport.Width;
      v->pos.y = (y + glyph->draw_offset_y) * scale / (float)d3d11->viewport.Height;
      v->pos.w = glyph->width * scale / (float)d3d11->viewport.Width;
      v->pos.h = glyph->height * scale / (float)d3d11->viewport.Height;

      v->coords.u = glyph->atlas_offset_x / (float)font->texture.desc.Width;
      v->coords.v = glyph->atlas_offset_y / (float)font->texture.desc.Height;
      v->coords.w = glyph->width / (float)font->texture.desc.Width;
      v->coords.h = glyph->height / (float)font->texture.desc.Height;

      v->params.scaling  = 1;
      v->params.rotation = 0;

      v->colors[0] = color;
      v->colors[1] = color;
      v->colors[2] = color;
      v->colors[3] = color;

      v++;

      x += glyph->advance_x * scale;
      y += glyph->advance_y * scale;
   }

   count = v - ((d3d11_sprite_t*)mapped_vbo.pData + d3d11->sprites.offset);
   D3D11UnmapBuffer(d3d11->context, d3d11->sprites.vbo, 0);

   if (!count)
      return;

   if (font->atlas->dirty)
   {
      d3d11_update_texture(
            d3d11->context, font->atlas->width, font->atlas->height, font->atlas->width,
            DXGI_FORMAT_A8_UNORM, font->atlas->buffer, &font->texture);
      font->atlas->dirty = false;
   }

   d3d11_set_texture_and_sampler(d3d11->context, 0, &font->texture);
   D3D11SetBlendState(d3d11->context, d3d11->blend_enable, NULL, D3D11_DEFAULT_SAMPLE_MASK);

   D3D11SetPShader(d3d11->context, d3d11->sprites.shader_font.ps, NULL, 0);
   D3D11Draw(d3d11->context, count, d3d11->sprites.offset);
   D3D11SetPShader(d3d11->context, d3d11->sprites.shader.ps, NULL, 0);

   d3d11->sprites.offset += count;
}
Ejemplo n.º 11
0
static void wiiu_font_render_line(
      video_frame_info_t *video_info,
      wiiu_font_t* font, const char* msg, unsigned msg_len,
      float scale, const unsigned int color, float pos_x,
      float pos_y, unsigned text_align)
{
   unsigned i;
   wiiu_video_t* wiiu = (wiiu_video_t*)video_driver_get_ptr(false);
   unsigned width   = video_info->width;
   unsigned height  = video_info->height;
   int x            = roundf(pos_x * width);
   int y            = roundf((1.0f - pos_y) * height);
   int delta_x      = 0;
   int delta_y      = 0;

   if(wiiu->vertex_cache.current + (msg_len * 4) > wiiu->vertex_cache.size)
      return;

   switch (text_align)
   {
      case TEXT_ALIGN_RIGHT:
         x -= wiiu_font_get_message_width(font, msg, msg_len, scale);
         break;

      case TEXT_ALIGN_CENTER:
         x -= wiiu_font_get_message_width(font, msg, msg_len, scale) / 2;
         break;
   }

   position_t* pos = wiiu->vertex_cache.positions + wiiu->vertex_cache.current;
   tex_coord_t* coord = wiiu->vertex_cache.tex_coords + wiiu->vertex_cache.current;

   for (i = 0; i < msg_len; i++)
   {
      int off_x, off_y, tex_x, tex_y, width, height;
      const char* msg_tmp            = &msg[i];
      unsigned code                  = utf8_walk(&msg_tmp);
      unsigned skip                  = msg_tmp - &msg[i];

      if (skip > 1)
         i += skip - 1;

      const struct font_glyph* glyph =
         font->font_driver->get_glyph(font->font_data, code);

      if (!glyph) /* Do something smarter here ... */
         glyph = font->font_driver->get_glyph(font->font_data, '?');

      if (!glyph)
         continue;

      off_x  = glyph->draw_offset_x;
      off_y  = glyph->draw_offset_y;
      tex_x  = glyph->atlas_offset_x;
      tex_y  = glyph->atlas_offset_y;
      width  = glyph->width;
      height = glyph->height;


      float x0 = x + off_x + delta_x * scale;
      float y0 = y + off_y + delta_y * scale + height * scale;
      float u0 = tex_x;
      float v0 = tex_y;
      float x1 = x0 + width * scale;
      float y1 = y0 - height * scale;
      float u1 = u0 + width;
      float v1 = v0 + height;

      pos[0].x = (2.0f * x0 / wiiu->color_buffer.surface.width) - 1.0f;
      pos[0].y = (-2.0f * y0 / wiiu->color_buffer.surface.height) + 1.0f;
      pos[1].x = (2.0f * x1 / wiiu->color_buffer.surface.width) - 1.0f;;
      pos[1].y = (-2.0f * y0 / wiiu->color_buffer.surface.height) + 1.0f;
      pos[2].x = (2.0f * x1 / wiiu->color_buffer.surface.width) - 1.0f;;
      pos[2].y = (-2.0f * y1 / wiiu->color_buffer.surface.height) + 1.0f;
      pos[3].x = (2.0f * x0 / wiiu->color_buffer.surface.width) - 1.0f;;
      pos[3].y = (-2.0f * y1 / wiiu->color_buffer.surface.height) + 1.0f;
      pos += 4;

      coord[0].u = u0 / font->texture.surface.width;
      coord[0].v = v1 / font->texture.surface.height;
      coord[1].u = u1 / font->texture.surface.width;
      coord[1].v = v1 / font->texture.surface.height;
      coord[2].u = u1 / font->texture.surface.width;
      coord[2].v = v0 / font->texture.surface.height;
      coord[3].u = u0 / font->texture.surface.width;
      coord[3].v = v0 / font->texture.surface.height;
      coord += 4;

      delta_x += glyph->advance_x;
      delta_y += glyph->advance_y;
   }

   int count = pos - wiiu->vertex_cache.positions - wiiu->vertex_cache.current;

   if (!count)
      return;


   GX2Invalidate(GX2_INVALIDATE_MODE_CPU_ATTRIBUTE_BUFFER, wiiu->vertex_cache.positions + wiiu->vertex_cache.current, count * sizeof(position_t));
   GX2Invalidate(GX2_INVALIDATE_MODE_CPU_ATTRIBUTE_BUFFER, wiiu->vertex_cache.tex_coords + wiiu->vertex_cache.current, count * sizeof(tex_coord_t));

   if(font->atlas->dirty)
   {
      for (i = 0; (i < font->atlas->height) && (i < font->texture.surface.height); i++)
         memcpy(font->texture.surface.image + (i * font->texture.surface.pitch),
                font->atlas->buffer + (i * font->atlas->width), font->atlas->width);

      GX2Invalidate(GX2_INVALIDATE_MODE_CPU_TEXTURE, font->texture.surface.image,
                    font->texture.surface.imageSize);
      font->atlas->dirty = false;
   }


#if 0
   printf("%s\n", msg);
   DEBUG_VAR(color);
#endif

   GX2SetPixelTexture(&font->texture, wiiu->shader->sampler.location);

   GX2SetBlendConstantColor(((color >> 0) & 0xFF) / 255.0f, ((color >> 8) & 0xFF) / 255.0f,
                            ((color >> 16) & 0xFF) / 255.0f, ((color >> 24) & 0xFF) / 255.0f);

   GX2SetBlendControl(GX2_RENDER_TARGET_0, GX2_BLEND_MODE_BLEND_FACTOR, GX2_BLEND_MODE_INV_SRC_ALPHA, GX2_BLEND_COMBINE_MODE_ADD,
                      GX2_ENABLE,          GX2_BLEND_MODE_SRC_ALPHA, GX2_BLEND_MODE_INV_SRC_ALPHA, GX2_BLEND_COMBINE_MODE_ADD);

   GX2DrawEx(GX2_PRIMITIVE_MODE_QUADS, count, wiiu->vertex_cache.current, 1);

   GX2SetBlendControl(GX2_RENDER_TARGET_0, GX2_BLEND_MODE_SRC_ALPHA, GX2_BLEND_MODE_INV_SRC_ALPHA, GX2_BLEND_COMBINE_MODE_ADD,
                      GX2_ENABLE,          GX2_BLEND_MODE_SRC_ALPHA, GX2_BLEND_MODE_INV_SRC_ALPHA, GX2_BLEND_COMBINE_MODE_ADD);

   wiiu->vertex_cache.current = pos - wiiu->vertex_cache.positions;
}