void Font::CalculateAdvances(ImageBuffer &image) { // Get the format and size of the surface. int width = image.Width() / GLYPHS; height = image.Height(); unsigned mask = 0xFF000000; unsigned half = 0xC0000000; int pitch = image.Width(); // advance[previous * GLYPHS + next] is the x advance for each glyph pair. // There is no advance if the previous value is 0, i.e. we are at the very // start of a string. memset(advance, 0, GLYPHS * sizeof(advance[0])); for(int previous = 1; previous < GLYPHS; ++previous) for(int next = 0; next < GLYPHS; ++next) { int maxD = 0; int glyphWidth = 0; uint32_t *begin = image.Pixels(); for(int y = 0; y < height; ++y) { // Find the last non-empty pixel in the previous glyph. uint32_t *pend = begin + previous * width; uint32_t *pit = pend + width; while(pit != pend && (*--pit & mask) < half) {} int distance = (pit - pend) + 1; glyphWidth = max(distance, glyphWidth); // Special case: if "next" is zero (i.e. end of line of text), // calculate the full width of this character. Otherwise: if(next) { // Find the first non-empty pixel in this glyph. uint32_t *nit = begin + next * width; uint32_t *nend = nit + width; while(nit != nend && (*nit++ & mask) < half) {} // How far apart do you want these glyphs drawn? If drawn at // an advance of "width", there would be: // pend + width - pit <- pixels after the previous glyph. // nit - (nend - width) <- pixels before the next glyph. // So for zero kerning distance, you would want: distance += 1 - (nit - (nend - width)); } maxD = max(maxD, distance); // Update the pointer to point to the beginning of the next row. begin += pitch; } // This is a fudge factor to avoid over-kerning, especially for the // underscore and for glyph combinations like AV. advance[previous * GLYPHS + next] = max(maxD, glyphWidth - 4) / 2; } // Set the space size based on the character width. width /= 2; height /= 2; space = (width + 3) / 6 + 1; }
void Font::Load(const string &imagePath) { // Load the texture. ImageBuffer image; if(!image.Read(imagePath)) return; LoadTexture(image); CalculateAdvances(image); SetUpShader(image.Width() / GLYPHS, image.Height()); }
void Font::LoadTexture(ImageBuffer &image) { glGenTextures(1, &texture); glBindTexture(GL_TEXTURE_2D, texture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, image.Width(), image.Height(), 0, GL_BGRA, GL_UNSIGNED_BYTE, image.Pixels()); }
void Font::Load(const string &imagePath) { // Load the texture. ImageBuffer *image = ImageBuffer::Read(imagePath); if(!image) return; LoadTexture(image); CalculateAdvances(image); SetUpShader(image->Width() / GLYPHS, image->Height()); delete image; }