2016-11-15 82 views
1

我正在嘗試使用OpenGL和SDL,使用SDL_ttf來渲染文本到紋理,但代碼呈現垃圾。SDL_TTF渲染垃圾到紋理

我的「紋理渲染代碼」:

GLuint textToTexture(std::string & text, TTF_Font* font, glm::vec4 textColour, glm::vec4 bgColour) 
{ 
    if (!TTF_WasInit()) 
    { 
     if (TTF_Init() == -1) 
      exit(6); 
    } 
    SDL_Color colour = { (Uint8)(textColour.r*255), (Uint8)(textColour.g*255), (Uint8)(textColour.b*255), (Uint8)(textColour.a*255) }; 
    SDL_Color bg = { (Uint8)(bgColour.r*255), (Uint8)(bgColour.g*255), (Uint8)(bgColour.b*255), (Uint8)(bgColour.a*255) }; 

    SDL_Surface *stringImage = NULL; 
    stringImage = TTF_RenderText_Blended(font, text.c_str(), colour); 

    if (stringImage == NULL) 
    { 
     exit(5); 
    } 

    GLuint trueH = powerofTwo(stringImage->h); 
    GLuint trueW = powerofTwo(stringImage->w); 
    unsigned char* pixels = NULL; 
    GLuint w = stringImage->w; 
    GLuint h = stringImage->h; 
    GLuint colours = stringImage->format->BytesPerPixel; 
    pixels = padTexture((unsigned char*)stringImage->pixels, w, h, pixels, trueW, trueH, colours); 
    GLuint format, internalFormat; 
    if (colours == 4) { 

     if (stringImage->format->Rmask == 0x000000ff) 
      format = GL_RGBA; 
     else 
      format = GL_BGRA; 
    } 
    else {  

     // no alpha 
     if (stringImage->format->Rmask == 0x000000ff) 
      format = GL_RGB; 
     else 
      format = GL_BGR; 
    } 
    internalFormat = (colours == 4) ? GL_RGBA : GL_RGB; 


    GLuint texId = 0; 
    //GLuint texture; 

    glGenTextures(1, &texId); 

    glBindTexture(GL_TEXTURE_2D, texId); 
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); 
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); 
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 
    glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, trueW, trueH, 0,format, GL_UNSIGNED_BYTE, pixels); 

    // SDL surface was used to generate the texture but is no longer 
    // required. Release it to free memory 
    SDL_FreeSurface(stringImage); 
    free(pixels) 
    return texId; 
} 

用於計算正確的尺寸爲填充的代碼:

int powerofTwo(int num) 
{ 
    if (num != 0) 
    { 
     num--; 
     num |= num >> 1; // Divide by 2^k for consecutive doublings of k up to 32, 
     num |= num >> 2; // and then or the results. 
     num |= num >> 4; 
     num |= num >> 8; 
     num |= num >> 16; 
     num++; 
    } 
    return num; 
} 

最後將代碼複製字節的紋理正確的尺寸:

unsigned char* padTexture(unsigned char * src, int srcW, int srcH, unsigned char * dest, int width, int height, int bpp) 
{ 
    dest = (unsigned char*)calloc(1, width*height*bpp); 
    for (int i = 0; i < srcH; i++) 
    { 
     memcpy(dest + (width*i*bpp),src + (srcW*i*bpp), srcW*bpp); 
    } 
    return dest; 
} 

該代碼的結果如下:[![Garbled Texture] [1 ]] [1]

我已確認並檢查錯誤,確定SDL_TTF在代碼庫中的其他位置正確初始化,並且字體也正在加載。 我用三種不同的ttf字體進行了測試,結果相同。

另外,如果我使用任何其他TTF_rendering功能(陰影,固體等),固體四被渲染,而在textToTexture功能的「顏色」變量也結束爲1

附加:

MavenPro正規,

HelveticaNeueLTStd釷

和另一個我發現了互聯網:

正如我前面提到的,我有三個ttf字體進行測試。 我試圖呈現字符串「Select Scenario」。

預填充的圖像尺寸是138x25像素。

後置填充的圖像尺寸是256x32像素。

更新1:

固定BPP問題後,新的紋理如下:

New garbled

此影像學改變,每次我運行程序。

更新2: 與填充的圖像,並且像素數據設置爲本身的質感,當我使用TTF_RenderText_Blended我得到的是一個黑色的方形,並固定更多的斑點錯誤後,當我使用TTF_RenderText_Shaded我得到:

enter image description here

更新3:

我immedietly調用GL代碼之前和呼叫SDL_RenderText_Blended後使用SDL_SaveBMP,結果是全白圖像,(給定哪個文本顏色)。

當我使用TTF_RenderText_Solid進行同樣的操作時,保存的圖像應該是它​​的樣子,但是像上面看到的圖像一樣由opengl渲染。

SDL_TTF初始化正常,字體加載沒有錯誤,並且文本渲染沒有返回錯誤,所以我想不出接下來要做什麼。

更新4:

因爲我已經重構所有的TTF代碼到一個單一的功能和去除填充代碼(如現代的OpenGL似乎並不關心它)。但是,儘管現在所有的項目設置和代碼都與已知可在同一硬件上工作的測試項目相同,但問題依然存在。

GLuint textToTexture(const char * text, const char * font, glm::vec4 textColour, glm::vec4 bgColour, unsigned int & texID) 
{ 
    if (!TTF_WasInit()) { 
     if (TTF_Init() == -1) 
      exit(6); 
    } 
    SDL_Color colour = { (Uint8)(textColour.r * 255), (Uint8)(textColour.g * 255), (Uint8)(textColour.b * 255),(Uint8)(textColour.a * 255) }; 
    SDL_Color bg = { (Uint8)(bgColour.r * 255), (Uint8)(bgColour.g * 255), (Uint8)(bgColour.b * 255),255 }; 
    TTF_Font* fontObj = TTF_OpenFont(font, 24); 
    if (!fontObj) 
    { 
     SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, 
      "Texture Error", 
      "Cannot load font to create texture.", 
      NULL); 
     return 0; 
    } 
    SDL_Surface *image = NULL; 
    image = TTF_RenderText_Blended(fontObj, text, colour); 
    if (image == NULL) 
    { 
     exit(5); 
     //exitFatalError("String surface not created."); 
     std::cout << "String surface not created." << std::endl; 

    } 
    unsigned char* pixels = NULL; 
    GLuint w = image->w; 
    GLuint h = image->h; 
    GLuint colours = image->format->BytesPerPixel; 
    GLuint externalFormat, internalFormat; 
    SDL_PixelFormat *format = image->format; 
    if (colours == 4) { 

     if (image->format->Rmask == 0x000000ff) 
      externalFormat = GL_RGBA; 
     else 
      externalFormat = GL_BGRA; 
    } 
    else { 

     // no alpha 
     if (image->format->Rmask == 0x000000ff) 
      externalFormat = GL_RGB; 
     else 
      externalFormat = GL_BGR; 
    } 
    internalFormat = (colours == 4) ? GL_RGBA : GL_RGB; 


    GLuint texId = 0; 
    //GLuint texture; 

    glGenTextures(1, &texID); 

    glBindTexture(GL_TEXTURE_2D, texID); 
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); 
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); 
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 
    glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, w, h, 0, externalFormat, GL_UNSIGNED_BYTE, image->pixels); 
    //glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, trueW, trueH, 0, externalFormat, GL_UNSIGNED_BYTE, pixels); 
    glGenerateMipmap(GL_TEXTURE_2D); 

    //// SDL surface was used to generate the texture but is no longer 
    //// required. Release it to free memory 
    SDL_FreeSurface(image); 
    TTF_CloseFont(fontObj); 
    return texID; 
} 

我有圖像保存爲BMP,然後重新加載它,並創建一個紋理的解決方法,但只有當我使用TTF_RenderText_Shaded。如果我使用TTF_RenderText_Blended,我會得到一個與文本顏色相對應的單色圖像。

+0

'SRC +(srcW * I)' - 你需要通過BPP乘過(這同樣適用於DST)。你也從來沒有釋放你的像素緩衝區,但它與你目前的問題無關。 – keltar

+0

好吧,我修正了這個錯誤(很好地發現btw),但它沒有改善產生的紋理,所以有可能還有一些其他問題? –

+0

從技術上講,你需要對齊行,但它們已經是bpp = 4。更新您所做的更改和結果的問題。瞭解原始表面的寬度和高度的實際值,使用哪種字體以及正在呈現哪些文本也可能會有幫助。 – keltar

回答

2
glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, trueH, trueW, 0,format, GL_UNSIGNED_BYTE, pixels); 

trueHtrueW順序顛倒

memcpy(src + (srcW*i*bpp), dest + (width*i*bpp), srcW*bpp); 

源和目的本末倒置。

dest = (unsigned char*)calloc(0, width*height*bpp); 

0個大小爲width*height*bpp的元素被分配,這是0個字節。應爲1而不是0

下面是一個完整的例子:

#include <SDL2/SDL.h> 
#include <GL/gl.h> 
#include <SDL2/SDL_ttf.h> 
#include <stdio.h> 
#include <string.h> 
#include <stdlib.h> 

static unsigned char* padTexture(unsigned char * src, int srcW, int srcH, unsigned char * dest, int width, int height, int bpp, const SDL_Palette *palette) 
{ 
    int dst_bpp = (bpp == 1) ? 4 : bpp; 
    dest = (unsigned char*)calloc(1, width*height*dst_bpp); 
    if(bpp != 1) { 
     for (int i = 0; i < srcH; i++) 
     { 
      memcpy(dest + (width*i*bpp), src + (srcW*i*bpp), srcW*bpp); 
     } 
    } else { 
     /* indexed - read colours from palette */ 
     for(int i = 0; i < srcH; i++) { 
      for(int j = 0; j < srcW; j++) { 
       memcpy(dest + (width*i+j)*dst_bpp, 
         &palette->colors[src[srcW*i+j]], sizeof(SDL_Color)); 
      } 
     } 
    } 
    return dest; 
} 

static int powerofTwo(int num) { 
    if (num != 0) 
    { 
     num--; 
     num |= num >> 1; // Divide by 2^k for consecutive doublings of k up to 32, 
     num |= num >> 2; // and then or the results. 
     num |= num >> 4; 
     num |= num >> 8; 
     num |= num >> 16; 
     num++; 
    } 
    return num; 
} 

static GLuint textToTexture(const char *text, TTF_Font* font) { 
    if (!TTF_WasInit()) { 
     if (TTF_Init() == -1) 
      exit(6); 
    } 
    SDL_Color colour = { 255, 255, 255, 255 }; 
    SDL_Color bg = { 0, 0, 0, 255 }; 

    SDL_Surface *stringImage = NULL; 
// stringImage = TTF_RenderText_Blended(font, text, colour); 
    stringImage = TTF_RenderText_Shaded(font, text, colour, bg); 

    if (stringImage == NULL) { 
     exit(5); 
    } 

    GLuint trueH = powerofTwo(stringImage->h); 
    GLuint trueW = powerofTwo(stringImage->w); 
    unsigned char* pixels = NULL; 
    GLuint w = stringImage->w; 
    GLuint h = stringImage->h; 
    GLuint colours = stringImage->format->BytesPerPixel; 
    pixels = padTexture((unsigned char*)stringImage->pixels, w, h, pixels, trueW, trueH, 
      colours, stringImage->format->palette); 
    GLuint format, internalFormat; 

    /* If indexed, want resulting image to be 32bit */ 
    if(colours == 1) { 
     colours = 4; 
    } 

    if (colours == 4) { 

     if (stringImage->format->Rmask == 0x000000ff) 
      format = GL_RGBA; 
     else 
      format = GL_BGRA; 
    } 
    else {  

     // no alpha 
     if (stringImage->format->Rmask == 0x000000ff) 
      format = GL_RGB; 
     else 
      format = GL_BGR; 
    } 
    internalFormat = (colours == 4) ? GL_RGBA : GL_RGB; 


    GLuint texId = 0; 
    //GLuint texture; 

    glGenTextures(1, &texId); 

    glBindTexture(GL_TEXTURE_2D, texId); 
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); 
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); 
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 
    glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, trueW, trueH, 0,format, GL_UNSIGNED_BYTE, pixels); 

    // SDL surface was used to generate the texture but is no longer 
    // required. Release it to free memory 
    SDL_FreeSurface(stringImage); 
    free(pixels); 
    return texId; 
} 

int main(int argc, char* argv[]) 
{ 
    SDL_Init(SDL_INIT_VIDEO); 
    TTF_Init(); 

    SDL_Window *window = SDL_CreateWindow("SDL2 Example", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 600, 400, SDL_WINDOW_OPENGL); 
    SDL_GLContext gl_ctx = SDL_GL_CreateContext(window); 

    TTF_Font *font = TTF_OpenFont(".fonts/tahoma.ttf", 16); 
    if(font) { 
     printf("font loaded\n"); 
     textToTexture("Select Scenario", font); 
     TTF_CloseFont(font); 
    } 

    int quit = 0; 
    while(!quit) { 
     SDL_Event ev; 
     while(SDL_PollEvent(&ev)) { 
      if(ev.type == SDL_QUIT || ev.type == SDL_KEYUP) { 
       quit = 1; 
      } 
     } 

     glClearColor(0.1f, 0.1f, 0.1f, 1.0f); 
     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 

     glLoadIdentity(); 
     glEnable(GL_BLEND); 
     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 
     glEnable(GL_TEXTURE_2D); 
     glColor3f(1.0f, 1.0f, 1.0f); 
     glBegin(GL_QUADS); 
      glTexCoord2f(0, 1); 
      glVertex2f(-0.5, -0.5); 
      glTexCoord2f(0, 0); 
      glVertex2f(-0.5, 0.5); 
      glTexCoord2f(1, 0); 
      glVertex2f(0.5, 0.5); 
      glTexCoord2f(1, 1); 
      glVertex2f(0.5, -0.5); 
     glEnd(); 

     glFlush(); 
     SDL_GL_SwapWindow(window); 
    } 

    SDL_GL_DeleteContext(gl_ctx); 
    SDL_DestroyWindow(window); 

    TTF_Quit(); 
    SDL_Quit(); 

    return 0; 
} 
+0

很好的發現。更正了這些錯誤但沒有明顯的改進,所以我用新代碼更新了這個問題。 –

+1

適合我。然後做一個完整的例子。 – keltar

+0

我檢查過存儲在GPU內存中的數據,並且圖像也出現亂碼,所以我猜測問題是使用SDL_TTF本身,還是將數據複製到GPU。您使用的是哪個版本的SDL_TTF? –