linux中opengl实现文字显示的方法

时间:2023-02-11 10:50:31

前段时间为了弄毕业设计,要用opengl显示文字,在网上找了很多方法,但大都是在windows下的实现方法,几乎没有linux下的,找了很久才找到。

首先,opengl本身是没有显示文字的函数的,必须要借助其他的库,linux下有SDL,利用SDL本身的编程是可以显示汉字的,但是如何将opengl和SDL融合显示汉字!这就是难点所在!

#include <SDL/SDL.h>
#include <SDL/SDL_image.h>
#include <SDL/SDL_ttf.h>
#include <SDL/SDL_opengl.h>
#include <wchar.h>
#include <iconv.h>

#define TRUE (-1)
#define FALSE 0
typedef int BOOL;


const int SCREEN_WIDTH = 600;
const int SCREEN_HEIGHT = 400;
const int SCREEN_BPP = 32;

SDL_Surface *image = NULL;
SDL_Surface *screen = NULL;
SDL_Surface *message = NULL;

TTF_Font *font = NULL;
SDL_Color textColor = { 255, 0, 0 };
SDL_Color blackColor = { 0,0,0 };

BOOL init()
{

if( SDL_Init( SDL_INIT_EVERYTHING ) == -1 )
{
return FALSE;
}

//screen = SDL_SetVideoMode( SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP, SDL_SWSURFACE );
screen = SDL_SetVideoMode( SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP, SDL_OPENGL );

if( screen == NULL )
{
return FALSE;
}

//Initialize SDL_ttf
if( TTF_Init() == -1 )
return FALSE;

SDL_WM_SetCaption( "show Chinese text @bluedrum", NULL );
//If everything initialized fine
return TRUE;
}

void clean_up()
{

SDL_FreeSurface( image );
SDL_FreeSurface( message );
TTF_CloseFont( font ); //Close the font that was used
TTF_Quit(); //Quit SDL_ttf
SDL_Quit(); //Quit SDL
}

SDL_Surface *load_image( char * filename )
{
SDL_Surface* loadedImage = NULL;

SDL_Surface* optimizedImage = NULL;

loadedImage = IMG_Load( filename);

if( loadedImage != NULL )
{
optimizedImage = SDL_DisplayFormat( loadedImage );

SDL_FreeSurface( loadedImage );
}

return optimizedImage;
}

void SDL_GL_Enter2DMode()
{
SDL_Surface *screen = SDL_GetVideoSurface();

/* Note, there may be other things you need to change,
depending on how you have your OpenGL state set up.
*/
glPushAttrib(GL_ENABLE_BIT);
glDisable(GL_DEPTH_TEST);
glDisable(GL_CULL_FACE);
glEnable(GL_TEXTURE_2D);

/* This allows alpha blending of 2D textures with the scene */
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glViewport(0, 0, screen->w, screen->h);

glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();

glOrtho(0.0, (GLdouble)screen->w, (GLdouble)screen->h, 0.0, 0.0, 1.0);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();

glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
}

void SDL_GL_Leave2DMode()
{
glMatrixMode(GL_MODELVIEW);
glPopMatrix();

glMatrixMode(GL_PROJECTION);
glPopMatrix();
glPopAttrib();
}

/* Quick utility function for texture creation */
static int power_of_two(int input)
{
int value = 1;

while ( value < input ) {
value <<= 1;
}
return value;
}

GLuint SDL_GL_LoadTexture(SDL_Surface *surface, GLfloat *texcoord)
{
GLuint texture;
int w, h;
SDL_Surface *image;
SDL_Rect area;
Uint32 saved_flags;
Uint8 saved_alpha;

/* Use the surface width and height expanded to powers of 2 */
w = power_of_two(surface->w);
h = power_of_two(surface->h);
texcoord[0] = 0.0f; /* Min X */
texcoord[1] = 0.0f; /* Min Y */
texcoord[2] = (GLfloat)surface->w / w; /* Max X */
texcoord[3] = (GLfloat)surface->h / h; /* Max Y */

image = SDL_CreateRGBSurface(
SDL_SWSURFACE,
w, h,
32,
#if SDL_BYTEORDER == SDL_LIL_ENDIAN /* OpenGL RGBA masks */
0x000000FF,
0x0000FF00,
0x00FF0000,
0xFF000000
#else
0xFF000000,
0x00FF0000,
0x0000FF00,
0x000000FF
#endif
);
if ( image == NULL ) {
return 0;
}

/* Save the alpha blending attributes */
saved_flags = surface->flags&(SDL_SRCALPHA|SDL_RLEACCELOK);
#if SDL_VERSION_ATLEAST(1, 3, 0)
SDL_GetSurfaceAlphaMod(surface, &saved_alpha);
SDL_SetSurfaceAlphaMod(surface, 0xFF);
#else
saved_alpha = surface->format->alpha;
if ( (saved_flags & SDL_SRCALPHA) == SDL_SRCALPHA ) {
SDL_SetAlpha(surface, 0, 0);
}
#endif

/* Copy the surface into the GL texture image */
area.x = 0;
area.y = 0;
area.w = surface->w;
area.h = surface->h;
SDL_BlitSurface(surface, &area, image, &area);

/* Restore the alpha blending attributes */
#if SDL_VERSION_ATLEAST(1, 3, 0)
SDL_SetSurfaceAlphaMod(surface, saved_alpha);
#else
if ( (saved_flags & SDL_SRCALPHA) == SDL_SRCALPHA ) {
SDL_SetAlpha(surface, saved_flags, saved_alpha);
}
#endif

/* Create an OpenGL texture for the image */
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D,
0,
GL_RGBA,
w, h,
0,
GL_RGBA,
GL_UNSIGNED_BYTE,
image->pixels);
SDL_FreeSurface(image); /* No longer needed */

return texture;
}



int main(int argc,char * argv[])
{
char a[] = "abc";
char *img_name = "./bk.jpg";
Uint16 msg[1024] = {0xc2d0,0xcbce,0xcce5,0};
wchar_t *msg1 = L"新宋体";
int w,h,done;
int i=0;
GLuint texture;
GLfloat texcoord[4];
GLfloat texMinX, texMinY;
GLfloat texMaxX, texMaxY;
SDL_Event event;

//msg2 = ConvertUnicodeToUtf8(msg1);

init();

if(argc > 1)
{
strncpy(img_name,argv[1],sizeof(img_name)-1);
img_name[sizeof(img_name)-1] = 0;

}

font = TTF_OpenFont("./simfang.ttf",20);
if(font == NULL)
{
fprintf(stderr,"font open failure %s\n",SDL_GetError());
clean_up();
exit(-1);
}

image =load_image(img_name);
TTF_SetFontStyle(font,TTF_STYLE_BOLD | TTF_STYLE_ITALIC);
message = TTF_RenderUNICODE_Solid( font, msg1, textColor );
//message = TTF_RenderText_Solid(font,"ABCEFGHIJK",blackColor);

w = message->w;
h = message->h;
//w = image->w;
//h = image->h;

texture = SDL_GL_LoadTexture(message, texcoord);

/* Make texture coordinates easy to understand */
texMinX = texcoord[0];
texMinY = texcoord[1];
texMaxX = texcoord[2];
texMaxY = texcoord[3];

/* Initialize the GL state */
glViewport( 0, 0, screen->w, screen->h );
glMatrixMode( GL_PROJECTION );
glLoadIdentity( );

glOrtho( -2.0, 2.0, -2.0, 2.0, -20.0, 20.0 );

glMatrixMode( GL_MODELVIEW );
glLoadIdentity( );

glEnable(GL_DEPTH_TEST);

glDepthFunc(GL_LESS);

glShadeModel(GL_SMOOTH);

/* Clear the screen */
glClearColor(1.0, 1.0, 1.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

/* Show the text on the screen 关键部分*/
SDL_GL_Enter2DMode();
glBindTexture(GL_TEXTURE_2D, texture);
glBegin(GL_TRIANGLE_STRIP);
glTexCoord2f(texMinX, texMinY); glVertex2i(50, 50 );
glTexCoord2f(texMaxX, texMinY); glVertex2i(50+w, 50 );
glTexCoord2f(texMinX, texMaxY); glVertex2i(50, 50+h);
glTexCoord2f(texMaxX, texMaxY); glVertex2i(50+w, 50+h);
glEnd();
SDL_GL_Leave2DMode();

SDL_GL_SwapBuffers( );


if(SDL_Flip(screen) == -1)
return -1;

/* Wait for a keystroke,input esc to quit */
done = 0;
while ( ! done ) {
while ( SDL_PollEvent(&event) ) {
switch (event.type) {
case SDL_KEYDOWN:
case SDL_QUIT:
done = 1;
break;
default:
break;
}
}
}
//SDL_Delay(3000);

clean_up();

}

编译用gcc test.c -o test -lSDL_image -lSDL_ttf -lglut -finput-charset='gbk' -fshort-wchar

前面几个参数是调用库, -finput-charset='gbk' -fshort-wchar参数是为了解决汉字的编码问题

其中bk.jpg是图片文件./simfang.tff是汉字库,可以在网上下。

代码分析,与其它的opengl函数使用来说就多了几个函数,SDL_GL_LoadTexture;SDL_GL_Enter2DMode;SDL_GL_Leave2DMode这3个函数,它们是实现汉字显示的关键函数,函数之间相互独立,可以拷过去直接用。其根本原理还是使用了opengl的纹理映射的原理;图片和汉字的用法也是一样的。PS:前面做的时候,看到过中科院一个硕士的方法,看了半天论文也没发现具体的实现方法,汗!!最后还是在SDL的官方文件中找到,再自己研究的方法。

opengl和SDL的融合还是在 SDL_SetVideoMode( SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP, SDL_OPENGL )中的SDL_OPENGL宏,否则就是SDL的显示了。如果能用SDL加opengl来作界面和游戏的话,还是很给力的。