使用SDL2 TTF和glDrawPixels渲染文本。

huangapple go评论81阅读模式
英文:

Render Text using SDL2 TTF and glDrawPixels

问题

我目前正在为一个库添加对SDL2窗口系统的支持,该库目前使用GLUT。这包括渲染文本。问题的关键部分是,我无法在文本渲染发生的位置访问WindowRenderer。以前,使用GLUT的glutBitmapCharacter函数渲染文本。

我非常接近解决这个问题,通过使用glDrawPixels,但输出看起来像这样:
使用SDL2 TTF和glDrawPixels渲染文本。

似乎纹理被翻转,上方渲染了一些噪音。我尝试了一些GL_UNPACK_XXX函数的设置,但无法找出如何修复这个问题。你有什么想法吗?

以下是一个能够复现此问题的示例应用程序:

main.cpp:

#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h>
#include <GL/glew.h>
#include <string>
#include <stdint.h>
#include <chrono>
#include <thread>

const int width = 400;
const int height = 400;

TTF_Font* font = nullptr;

// 此函数是我唯一可以更改的部分!
void drawText(std::string text) {
  SDL_Color color = {
    static_cast<Uint8>(255),
    static_cast<Uint8>(0),
    static_cast<Uint8>(255),
    static_cast<Uint8>(255)
  };

  SDL_Surface* textRender = TTF_RenderUTF8_Blended(font, text.c_str(), color);
  SDL_Surface* surface = SDL_ConvertSurfaceFormat(textRender, SDL_PIXELFORMAT_RGBA8888, 0);
  SDL_FreeSurface(textRender);

  glPushAttrib(GL_BLEND);
  glPushAttrib(GL_BLEND_EQUATION);
  glPushAttrib(GL_CLIENT_PIXEL_STORE_BIT);

  glEnable(GL_BLEND);
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

  glPixelStorei(GL_UNPACK_SWAP_BYTES,  GL_FALSE);
  glPixelStorei(GL_UNPACK_LSB_FIRST,   GL_FALSE);
  glPixelStorei(GL_UNPACK_ROW_LENGTH,  surface->pitch);
  glPixelStorei(GL_UNPACK_SKIP_ROWS,   0);
  glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
  glPixelStorei(GL_UNPACK_ALIGNMENT,   1);

  glDrawPixels(surface->w, surface->h, GL_RGBA, GL_UNSIGNED_BYTE, surface->pixels);

  glPopAttrib();
  glPopAttrib();
  glPopAttrib();

  SDL_FreeSurface(surface);
}

// 下面的一切只是为了提供一个最小的可复现示例!

void loop(int64_t frame) {
  glClearColor(1.0, 1.0, 1.0, 1.0);
  glClear(GL_COLOR_BUFFER_BIT);
  glRasterPos2f(-1.0, 0);
  drawText(std::string("Hello World!") + std::to_string(frame));
}

int main() {
  SDL_InitSubSystem(SDL_INIT_VIDEO);
  TTF_Init();

  auto* window = SDL_CreateWindow("sdl-text", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, SDL_WINDOW_OPENGL);
  auto* context = SDL_GL_CreateContext(window);
  glewInit();

  bool quit = false;
  int64_t frame = 0;

  font = TTF_OpenFont("arial.ttf", 42);
  if (!font) {
    quit = true;
  }

  while (!quit) {
    loop(frame++);

    SDL_GL_SwapWindow(window);

    SDL_Event e;
    while (SDL_PollEvent(&e)) {
      switch(e.type) {
        case SDL_QUIT:
          quit = true;
          break;
      }
    }

    std::this_thread::sleep_for(std::chrono::milliseconds(16));
  }

  TTF_Quit();
  SDL_GL_DeleteContext(context);
  SDL_DestroyWindow(window);
  SDL_QuitSubSystem(SDL_INIT_VIDEO);

  return 0;
}

使用以下命令编译:

g++ main.cpp -lSDL2 -lSDL2_ttf -lGLEW -lGL

<details>
<summary>英文:</summary>
I am currently adding support for a SDL2 windowing system to a library, that currently uses GLUT. This includes rendering text. The problematic part is that I don&#39;t have access to the `Window` or `Renderer` at the position where text rendering happens. Previously text was rendered with GLUT using the `glutBitmapCharacter` function.
I am very close to solving this, by using `glDrawPixels`, but the output looks like this:
[![Output][1]][1]
It seems, that the texture is flipped and some noise is rendered above. I tried playing with the `GL_UNPACK_XXX` functions, but couldn&#39;t find out how to fix that. Do you have some idea?
Here is a sample application, that reproduces this issuse:
`main.cpp:`
```c++
#include &lt;SDL2/SDL.h&gt;
#include &lt;SDL2/SDL_ttf.h&gt;
#include &lt;GL/glew.h&gt;
#include &lt;string&gt;
#include &lt;stdint.h&gt;
#include &lt;chrono&gt;
#include &lt;thread&gt;
const int width = 400;
const int height = 400;
TTF_Font* font = nullptr;
// THIS FUNCTION IS THE ONLY THING I CAN CHANGE!
void drawText(std::string text) {
SDL_Color color = {
static_cast&lt;Uint8&gt;(255),
static_cast&lt;Uint8&gt;(0),
static_cast&lt;Uint8&gt;(255),
static_cast&lt;Uint8&gt;(255)
};
SDL_Surface* textRender = TTF_RenderUTF8_Blended(font, text.c_str(), color);
SDL_Surface* surface = SDL_ConvertSurfaceFormat(textRender, SDL_PIXELFORMAT_RGBA8888, 0);
SDL_FreeSurface(textRender);
glPushAttrib(GL_BLEND);
glPushAttrib(GL_BLEND_EQUATION);
glPushAttrib(GL_CLIENT_PIXEL_STORE_BIT);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glPixelStorei(GL_UNPACK_SWAP_BYTES,  GL_FALSE);
glPixelStorei(GL_UNPACK_LSB_FIRST,   GL_FALSE);
glPixelStorei(GL_UNPACK_ROW_LENGTH,  surface-&gt;pitch);
glPixelStorei(GL_UNPACK_SKIP_ROWS,   0);
glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
glPixelStorei(GL_UNPACK_ALIGNMENT,   1);
glDrawPixels(surface-&gt;w, surface-&gt;h, GL_RGBA, GL_UNSIGNED_BYTE, surface-&gt;pixels);
glPopAttrib();
glPopAttrib();
glPopAttrib();
SDL_FreeSurface(surface);
}
// EVERYTHING BELOW HERE IS ONLY TO PROVIDE A MINIMUM REPRODUCABLE EXAMPLE!
void loop(int64_t frame) {
glClearColor(1.0, 1.0, 1.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
glRasterPos2f(-1.0, 0);
drawText(std::string(&quot;Hello World!&quot;) + std::to_string(frame));
}
int main() {
SDL_InitSubSystem(SDL_INIT_VIDEO);
TTF_Init();
auto* window = SDL_CreateWindow(&quot;sdl-text&quot;, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, SDL_WINDOW_OPENGL);
auto* context = SDL_GL_CreateContext(window);
glewInit();
bool quit = false;
int64_t frame = 0;
font = TTF_OpenFont(&quot;arial.ttf&quot;, 42);
if (!font) {
quit = true;
}
while (!quit) {
loop(frame++);
SDL_GL_SwapWindow(window);
SDL_Event e;
while (SDL_PollEvent(&amp;e)) {
switch(e.type) {
case SDL_QUIT:
quit = true;
break;
}
}
std::this_thread::sleep_for(std::chrono::milliseconds(16));
}
TTF_Quit();
SDL_GL_DeleteContext(context);
SDL_DestroyWindow(window);
SDL_QuitSubSystem(SDL_INIT_VIDEO);
return 0;
}

compiled with the following command:

g++ main.cpp -lSDL2 -lSDL2_ttf -lGLEW -lGL

答案1

得分: 1

好的,以下是已翻译的部分:

"Ok, it seems like OpenGL takes the row length in pixel and SDL provides it in bytes. This is easily solved."

"我们还不需要将表面转换为RGBA。我们只需告诉OpenGL绘制BGRA(尽管SDL说它提供AGBR,但它也有效)。

"Finally we flip the image before drawing with glPixelZoom(1, -1). To offset the flip we have to move it up by the height."

"The solution is the following:"

"void drawText(std::string text) {"

"SDL_Color color = {"

"static_cast(255),"

"static_cast(0),"

"static_cast(0),"

"};"

"// We don't need to convert the surface format."

"SDL_Surface* surface = TTF_RenderUTF8_Blended(font, text.c_str(), color);"

"glPushAttrib(GL_BLEND);"

"glPushAttrib(GL_BLEND_EQUATION);"

"glPushAttrib(GL_CLIENT_PIXEL_STORE_BIT);"

"glEnable(GL_BLEND);"

"glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);"

"// SDL uses bytes, while OpenGL uses pixels."

"glPixelStorei(GL_UNPACK_ROW_LENGTH, surface->pitch / surface->format->BytesPerPixel);"

"// Get the current raster position."

"std::array<float, 4> pos;"

"glGetFloatv(GL_CURRENT_RASTER_POSITION, pos.data());"

"// Because the texture is upside down we move it up by the height and flip it."

"glWindowPos2f(pos[0], pos1 + surface->h);"

"glPixelZoom(1, -1);"

"// Use BGRA as a format."

"glDrawPixels(surface->w, surface->h, GL_BGRA, GL_UNSIGNED_BYTE, surface->pixels);"

"glPopAttrib();"

"glPopAttrib();"

"glPopAttrib();"

"SDL_FreeSurface(surface);"

"}"

英文:

Ok, it seems like OpenGL takes the row length in pixel and SDL provides it in bytes. This is easily solved.

We also don't need to convert the surface to RGBA. We can just tell OpenGL to draw BGRA (although SDL says it provides AGBR, but it works).

Finally we flip the image before drawing with glPixelZoom(1, -1).
To offset the flip we have to move it up by the height.

The solution is the following:

void drawText(std::string text) {
  SDL_Color color = {
    static_cast&lt;Uint8&gt;(255),
    static_cast&lt;Uint8&gt;(0),
    static_cast&lt;Uint8&gt;(0),
  };

  // We dont&#39;t need to convert the surface format.
  SDL_Surface* surface = TTF_RenderUTF8_Blended(font, text.c_str(), color);

  glPushAttrib(GL_BLEND);
  glPushAttrib(GL_BLEND_EQUATION);
  glPushAttrib(GL_CLIENT_PIXEL_STORE_BIT);

  glEnable(GL_BLEND);
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

  // SDL uses bytes, while OpenGL uses pixels.
  glPixelStorei(GL_UNPACK_ROW_LENGTH, surface-&gt;pitch / surface-&gt;format-&gt;BytesPerPixel);

  // Get the current raster position.
  std::array&lt;float, 4&gt; pos;
  glGetFloatv(GL_CURRENT_RASTER_POSITION, pos.data());

  // Because the texture is upside down we move it up by the height and flip it.
  glWindowPos2f(pos[0], pos[1] + surface-&gt;h);
  glPixelZoom(1, -1);

  // Use BGRA as a format.
  glDrawPixels(surface-&gt;w, surface-&gt;h, GL_BGRA, GL_UNSIGNED_BYTE, surface-&gt;pixels);

  glPopAttrib();
  glPopAttrib();
  glPopAttrib();

  SDL_FreeSurface(surface);
}

huangapple
  • 本文由 发表于 2023年7月7日 04:23:50
  • 转载请务必保留本文链接:https://go.coder-hub.com/76632309.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定