/** * From the OpenGL Programming wikibook: http://en.wikibooks.org/wiki/OpenGL_Programming * This file is in the public domain. * Contributors: Guus Sliepen, Sylvain Beucler */ #include #include using namespace std; /* Use glew.h instead of gl.h to get all the GL prototypes declared */ #include /* Using SDL2 for the base window and OpenGL context init */ #include "SDL.h" #include #include #include #include "../common-sdl2/shader_utils.h" #include #include FT_FREETYPE_H int screen_width=800, screen_height=600; GLuint program; GLint attribute_coord; GLint uniform_tex; GLint uniform_color; struct point { GLfloat x; GLfloat y; GLfloat s; GLfloat t; }; GLuint vbo; FT_Library ft; FT_Face face; const char *fontfilename; bool init_resources() { /* Initialize the FreeType2 library */ if (FT_Init_FreeType(&ft)) { cerr << "Could not init freetype library" << endl; return false; } /* Load a font */ int fontsize; char* font = file_read(fontfilename, &fontsize); if (font == NULL) { cerr << "Could not load font file " << fontfilename << endl; return false; } FT_Error fterr = FT_New_Memory_Face(ft, (FT_Byte*)font, fontsize, 0, &face); if (fterr != FT_Err_Ok) { cerr << "Could not init font: error 0x" << hex << fterr << endl; return false; } program = create_program("text.v.glsl", "text.f.glsl"); if(program == 0) return false; attribute_coord = get_attrib(program, "coord"); uniform_tex = get_uniform(program, "tex"); uniform_color = get_uniform(program, "color"); if(attribute_coord == -1 || uniform_tex == -1 || uniform_color == -1) return false; // Create the vertex buffer object glGenBuffers(1, &vbo); return true; } /** * Render text using the currently loaded font and currently set font size. * Rendering starts at coordinates (x, y), z is always 0. * The pixel coordinates that the FreeType2 library uses are scaled by (sx, sy). */ void render_text(const char *text, float x, float y, float sx, float sy) { const char *p; FT_GlyphSlot g = face->glyph; /* Create a texture that will be used to hold one "glyph" */ GLuint tex; glActiveTexture(GL_TEXTURE0); glGenTextures(1, &tex); glBindTexture(GL_TEXTURE_2D, tex); glUniform1i(uniform_tex, 0); /* We require 1 byte alignment when uploading texture data */ glPixelStorei(GL_UNPACK_ALIGNMENT, 1); /* Clamping to edges is important to prevent artifacts when scaling */ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); /* Linear filtering usually looks best for text */ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); /* Set up the VBO for our vertex data */ glEnableVertexAttribArray(attribute_coord); glBindBuffer(GL_ARRAY_BUFFER, vbo); glVertexAttribPointer(attribute_coord, 4, GL_FLOAT, GL_FALSE, 0, 0); /* Loop through all characters */ for (p = text; *p; p++) { /* Try to load and render the character */ if (FT_Load_Char(face, *p, FT_LOAD_RENDER)) continue; /* Upload the "bitmap", which contains an 8-bit grayscale image, as an alpha texture */ glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, g->bitmap.width, g->bitmap.rows, 0, GL_ALPHA, GL_UNSIGNED_BYTE, g->bitmap.buffer); /* Calculate the vertex and texture coordinates */ float x2 = x + g->bitmap_left * sx; float y2 = -y - g->bitmap_top * sy; float w = g->bitmap.width * sx; float h = g->bitmap.rows * sy; point box[4] = { {x2, -y2, 0, 0}, {x2 + w, -y2, 1, 0}, {x2, -y2 - h, 0, 1}, {x2 + w, -y2 - h, 1, 1}, }; /* Draw the character on the screen */ glBufferData(GL_ARRAY_BUFFER, sizeof box, box, GL_DYNAMIC_DRAW); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); /* Advance the cursor to the start of the next character */ x += (g->advance.x >> 6) * sx; y += (g->advance.y >> 6) * sy; } glDisableVertexAttribArray(attribute_coord); glDeleteTextures(1, &tex); } void render(SDL_Window* window) { float sx = 2.0 / screen_width; float sy = 2.0 / screen_height; glUseProgram(program); /* White background */ glClearColor(1, 1, 1, 1); glClear(GL_COLOR_BUFFER_BIT); /* Enable blending, necessary for our alpha texture */ glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); GLfloat black[4] = { 0, 0, 0, 1 }; GLfloat red[4] = { 1, 0, 0, 1 }; GLfloat transparent_green[4] = { 0, 1, 0, 0.5 }; /* Set font size to 48 pixels, color to black */ FT_Set_Pixel_Sizes(face, 0, 48); glUniform4fv(uniform_color, 1, black); /* Effects of alignment */ render_text("The Quick Brown Fox Jumps Over The Lazy Dog", -1 + 8 * sx, 1 - 50 * sy, sx, sy); render_text("The Misaligned Fox Jumps Over The Lazy Dog", -1 + 8.5 * sx, 1 - 100.5 * sy, sx, sy); /* Scaling the texture versus changing the font size */ render_text("The Small Texture Scaled Fox Jumps Over The Lazy Dog", -1 + 8 * sx, 1 - 175 * sy, sx * 0.5, sy * 0.5); FT_Set_Pixel_Sizes(face, 0, 24); render_text("The Small Font Sized Fox Jumps Over The Lazy Dog", -1 + 8 * sx, 1 - 200 * sy, sx, sy); FT_Set_Pixel_Sizes(face, 0, 48); render_text("The Tiny Texture Scaled Fox Jumps Over The Lazy Dog", -1 + 8 * sx, 1 - 235 * sy, sx * 0.25, sy * 0.25); FT_Set_Pixel_Sizes(face, 0, 12); render_text("The Tiny Font Sized Fox Jumps Over The Lazy Dog", -1 + 8 * sx, 1 - 250 * sy, sx, sy); FT_Set_Pixel_Sizes(face, 0, 48); /* Colors and transparency */ render_text("The Solid Black Fox Jumps Over The Lazy Dog", -1 + 8 * sx, 1 - 430 * sy, sx, sy); glUniform4fv(uniform_color, 1, red); render_text("The Solid Red Fox Jumps Over The Lazy Dog", -1 + 8 * sx, 1 - 330 * sy, sx, sy); render_text("The Solid Red Fox Jumps Over The Lazy Dog", -1 + 28 * sx, 1 - 450 * sy, sx, sy); glUniform4fv(uniform_color, 1, transparent_green); render_text("The Transparent Green Fox Jumps Over The Lazy Dog", -1 + 8 * sx, 1 - 380 * sy, sx, sy); render_text("The Transparent Green Fox Jumps Over The Lazy Dog", -1 + 18 * sx, 1 - 440 * sy, sx, sy); SDL_GL_SwapWindow(window); } void onResize(int width, int height) { screen_width = width; screen_height = height; glViewport(0, 0, screen_width, screen_height); } void free_resources() { glDeleteProgram(program); } void mainLoop(SDL_Window* window) { while (true) { SDL_Event ev; while (SDL_PollEvent(&ev)) { if (ev.type == SDL_QUIT) return; if (ev.type == SDL_WINDOWEVENT && ev.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) onResize(ev.window.data1, ev.window.data2); } render(window); } } int main(int argc, char *argv[]) { SDL_Init(SDL_INIT_VIDEO); SDL_Window* window = SDL_CreateWindow("Basic Text", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, screen_width, screen_height, SDL_WINDOW_RESIZABLE | SDL_WINDOW_OPENGL); if (window == NULL) { cerr << "Error: can't create window: " << SDL_GetError() << endl; return EXIT_FAILURE; } SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); //SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1); //SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); if (SDL_GL_CreateContext(window) == NULL) { cerr << "Error: SDL_GL_CreateContext: " << SDL_GetError() << endl; return EXIT_FAILURE; } GLenum glew_status = glewInit(); if (glew_status != GLEW_OK) { cerr << "Error: glewInit: " << glewGetErrorString(glew_status) << endl; return EXIT_FAILURE; } if (!GLEW_VERSION_2_0) { cerr << "Error: your graphic card does not support OpenGL 2.0" << endl; return EXIT_FAILURE; } if (argc > 1) fontfilename = argv[1]; else fontfilename = "FreeSans.ttf"; if (!init_resources()) return EXIT_FAILURE; mainLoop(window); free_resources(); return EXIT_SUCCESS; }