SDL_ttf: very stange bug with TTF_OpenFontIndexRW/TTF_OpenFontRW and render UTF8/Unicode string


#1

Hi!
Initially this bug was found under windows in my C# project used shared libs via wrapper SDL2-CS
SDL 2.0.8
SDL_ttf 2.0.15 / 2.0.14
After bug localization i could confirm it under linux in pure c.
Bug description:
If you use TTF_OpenFontIndexRW or TTF_OpenFontRW for creating TTF_Font with same SDL_RWops (for creating SDL_RWops I used SDL_RWFromFile, SDL_RWFromMem, SDL_RWFromConstMem) and you render text using TTF_RenderUTF8_Blended:

  • first TTF_Font font_2 with russian symbol й
  • second TTF_Font font_6 with any text: text will be blank(i set TTF_STYLE_UNDERLINE to second one to indicate that it was being rendered)

You got this:
image

If you don’t use russian symbol й in first render, you will get this:
will try add screenshot as comment

Finaly, if you use TTF_OpenFontIndex / TTF_OpenFont instead of TTF_OpenFontIndexRW / TTF_OpenFontRW, rendering works as it should:
will try add screenshot as comment

Hope you can help me, because using TTF_OpenFontIndex instead of TTF_OpenFontIndexRW is very expensive.

bug.c

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h>

int RenderText(SDL_Renderer *renderer, TTF_Font *font, char *text_line, int y, int style)
{
    SDL_Color textColor = {0x00, 0x00, 0x00, 0};
    TTF_SetFontStyle(font, style);
    SDL_Surface *textSurface = TTF_RenderUTF8_Blended(font, text_line, textColor);
    SDL_Texture *texture_text = SDL_CreateTextureFromSurface(renderer, textSurface);
    SDL_Rect dst_rect = {
        .y = y,
        .x = 0,
        .h = textSurface->h,
        .w = textSurface->w,
    };
    SDL_RenderCopy(renderer, texture_text, NULL, &dst_rect);
    SDL_DestroyTexture(texture_text);
    SDL_FreeSurface(textSurface);
    return dst_rect.h;
}
void bug(SDL_Renderer *renderer)
{

    int ptsize = 16;
    char *filename = "PT Serif_Italic.ttf";

    SDL_RWops *RWops = SDL_RWFromFile(filename, "rb");
    if (RWops == NULL)
        fprintf(stderr, "SDL_RWFromFile failed: %s\n", SDL_GetError());
    int y = 0;

    TTF_Font *font_2 = TTF_OpenFontIndexRW(RWops, 0, ptsize, 0);
    //TTF_Font *font_2 = TTF_OpenFontIndex(filename, ptsize, 0);
    if (font_2 == NULL)
        fprintf(stderr, "font_2 failed: %s\n", SDL_GetError());

    char *bug_str_char = "й";
    //char *bug_str_char = "Hello Привет";
    y += RenderText(renderer, font_2, bug_str_char, y, 2);

    TTF_Font *font_6 = TTF_OpenFontIndexRW(RWops, 0, ptsize, 0);
    //TTF_Font *font_6 = TTF_OpenFontIndex(filename, ptsize, 0);
    if (font_6 == NULL)
        fprintf(stderr, "font_6 failed: %s\n", SDL_GetError());

    y += RenderText(renderer, font_6, "Hello Привет", y, 6);
}
int main(int argc, char *argv[])
{
    SDL_Window *window;
    SDL_Renderer *renderer;
    SDL_Event event;

    SDL_Init(SDL_INIT_EVERYTHING);
    TTF_Init();

    if (SDL_CreateWindowAndRenderer(640, 480, 0, &window, &renderer) < 0)
        fprintf(stderr, "SDL_CreateWindowAndRenderer() failed: %s\n", SDL_GetError());

    int done = 0;
    while (!done)
    {
        if (SDL_WaitEvent(&event) < 0)
        {
            fprintf(stderr, "SDL_PullEvent() error: %s\n", SDL_GetError());
            done = 1;
            continue;
        }
        done = event.type == SDL_QUIT ? 1 : 0;
        SDL_SetRenderDrawColor(renderer, 0xFF, 0xFF, 0xFF, 0xFF);
        SDL_RenderClear(renderer);
        bug(renderer);
        SDL_RenderPresent(renderer);
        SDL_Delay(50);
    }
    TTF_Quit();
    SDL_Quit();
}


bug.c + PT Serif_Italic.ttf + Makefile: PT_Serif_Italic_Bug.zip


#2

If you don’t use russian symbol й in first render, you will get this:
image

Finaly, if you use TTF_OpenFontIndex / TTF_OpenFont instead of TTF_OpenFontIndexRW / TTF_OpenFontRW , rendering works as it should:
image


#3

In addition to my answers of https://bugzilla.libsdl.org/show_bug.cgi?id=4524

I think you could try to reproduce this with freetype only and submit them the issue
( https://savannah.nongnu.org/projects/freetype/ )

You don’t need to do draw/render text on screen, but you can just:
load the font, load the glyph, printf the metrics, and again second string.