Skip to content

Instantly share code, notes, and snippets.

@matthewjberger
Last active April 27, 2016 07:31
Show Gist options
  • Save matthewjberger/464281d2e9101d13f0c8e931948a2f4b to your computer and use it in GitHub Desktop.
Save matthewjberger/464281d2e9101d13f0c8e931948a2f4b to your computer and use it in GitHub Desktop.
Text Rendering with OpenGL
auto ReadAllBytes = [](const std::string& filename)
{
using namespace std;
ifstream file(filename.c_str(), ios::binary | ios::ate);
file >> noskipws;
streampos fileSize = file.tellg();
file.seekg(0, ios::beg);
vector<BYTE> bytes;
bytes.reserve(fileSize);
copy(istream_iterator<BYTE>(file),
istream_iterator<BYTE>(),
back_inserter(bytes));
return bytes;
};
auto bytes = ReadAllBytes("c:/windows/fonts/arialbd.ttf");
unsigned char* data = bytes.data();
stbtt_fontinfo font;
stbtt_InitFont(&font, data, 0);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
int scale = 20; int character = 'A';
unsigned char *bitmap = stbtt_GetCodepointBitmap(&font, 0, stbtt_ScaleForPixelHeight(&font, scale), character, &width_, &height_, 0, 0);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
create_from_data(width_, height_, bitmap, GL_RED, target);
return true;
#version 330 core
in vec2 f_texCoord;
out vec4 fragColor;
uniform sampler2D quadTexture;
uniform vec3 color;
void main()
{
vec4 sampled = vec4(1.0f, 1.0f, 1.0f, texture(quadTexture, f_texCoord).r); // use the red intensity as alpha channel
fragColor = vec4(color, 1.0f) * sampled; // color would be better suited for a uniform
/*
// Same thing as above, but in one line. Untested.
fragColor = vec4(color, texture(quadTexture, f_texCoord).r); // use only the r channel
*/
}
#version 330 core
layout (location = 0) in vec3 v_position;
layout (location = 1) in vec2 v_texCoord;
out vec2 f_texCoord;
uniform mat4 mvpMatrix;
void main()
{
gl_Position = mvpMatrix * vec4(v_position, 1.0f);
f_texCoord = vec2(v_texCoord.x, 1.0 - v_texCoord.y); // necessary to flip texture
}
#define STB_TRUETYPE_IMPLEMENTATION
#include "stb_truetype.h"
typedef unsigned char BYTE;
std::vector<BYTE> ReadAllBytes(const std::string& filename)
{
using namespace std;
ifstream file(filename.c_str(), ios::binary | ios::ate);
file >> noskipws;
streampos fileSize = file.tellg();
file.seekg(0, ios::beg);
vector<BYTE> bytes;
bytes.reserve(fileSize);
copy(istream_iterator<BYTE>(file),
istream_iterator<BYTE>(),
back_inserter(bytes));
return bytes;
}
// Read the file in as an array of bytes
auto bytes = ReadAllBytes("c:/windows/fonts/arialbd.ttf");
unsigned char* data = bytes.data(); // needs one level of indirection to work properly
/* STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset);
* Given an offset into the file that defines a font, this function builds
* the necessary cached info for the rest of the system. You must allocate
* the stbtt_fontinfo yourself, and stbtt_InitFont will fill it out. You don't
* need to do anything special to free it, because the contents are pure
* value data with no additional data structures. Returns 0 on failure.
*/
// Load the font information
stbtt_fontinfo font;
stbtt_InitFont(&font, data, 0);
/* STBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff);
allocates a large-enough single-channel 8bpp bitmap and renders the
specified character/glyph at the specified scale into it, with
antialiasing. 0 is no coverage (transparent), 255 is fully covered (opaque).
*width & *height are filled out with the width & height of the bitmap,
which is stored left-to-right, top-to-bottom.
xoff/yoff are the offset it pixel space from the glyph origin to the top-left of the bitmap
*/
// Create the glyph bitmap
int scale = 20; int character = 'A';
unsigned char *bitmap = stbtt_GetCodepointBitmap(&font, 0, stbtt_ScaleForPixelHeight(&font, scale), character, &width, &height, 0, 0);
/* The bitmap we just generated is a single channel, 8bit per pixel bitmap.
* 8 bits is equivalent to 1 byte. Since we are using 1 byte to describe each pixel's color
* and OpenGL's default unpacking alignment is 4 bytes to describe each pixel's color (32 bits per pixel)
* we have to manually change the unpacking alignment to unpack 1 byte at a time.
* This allows for 1 byte to describe each pixel.
*/
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
// Use our bitmap data to create a texture. Since we are using a single byte per pixel, it corresponds to the red component.
//(4 bytes would use r,g,b,a and each component is 1 byte)
glTexImage2D(target, 0, GL_RED, width_, height_, 0, GL_RED, GL_UNSIGNED_BYTE, bitmap);
// Enable blending. This will allow the alpha channel to take effect in the shader.
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
/* STBTT_DEF void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing);
* leftSideBearing is the offset from the current horizontal position to the left edge of the character
* advanceWidth is the offset from the current horizontal position to the next horizontal position
* these are expressed in unscaled coordinates SO MAKE SURE TO MULTIPLY BY SCALE VALUE
*/
int advanceWidth, leftSideBearing;
stbtt_GetCodepointHMetrics(&info, 'A', &advanceWidth, &leftSideBearing);
/* NOTE: The following code isn't necessary, but it may be useful.
* The width and height are filled out when you create the bitmap from the codepoint.
* STBTT_DEF int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1);
* Gets the bounding box of the visible part of the glyph, in unscaled coordinates SO MAKE SURE TO MULTIPLY BY SCALE VALUE
*/
int x0, y0, x1, y1;
stbtt_GetCodepointBox(&info, 'A', &x0, &y0, &x1, &y1);
// Reset the unpacking alignment back to 4 bytes (32 bits per pixel)
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
// Disable blending when finished
glEnable(GL_BLEND);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment