Page Menu
Home
Phabricator (Chris)
Search
Configure Global Search
Log In
Files
F118090
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
12 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/source/game/render/font_ft2.cpp b/source/game/render/font_ft2.cpp
index 632a01a..0e95fb5 100644
--- a/source/game/render/font_ft2.cpp
+++ b/source/game/render/font_ft2.cpp
@@ -1,487 +1,487 @@
#include "render/font.h"
#include "render/driver.h"
#include "render/vertexBuffer.h"
#include "main/initialization.h"
#include <stdio.h>
#include "str_util.h"
#include "vec2.h"
#ifdef _MSC_VER
#include <ft2build.h>
#else
#include <freetype2/ft2build.h>
#endif
#include FT_FREETYPE_H
#include FT_SIZES_H
#include <map>
#include <algorithm>
namespace resource {
extern render::Texture* queueImage(Image* img, int priority, bool mipmap, bool cachePixels);
};
namespace render {
Font* Font::createDummyFont() {
return new Font();
}
bool clipQuad(rectf pos, rectf source, const rectf* clip, vec2f* verts, vec2f* texcoords) {
if(clip) {
if(!clip->overlaps(pos))
return false;
if(!clip->isRectInside(pos)) {
//TODThere seem to be some oddities with the texture coordinates if a clip occurs
rectf clipped = clip->clipAgainst(pos);
source = source.clipProportional(pos, clipped);
pos = clipped;
}
}
verts[0] = vec2f(pos.topLeft.x, pos.topLeft.y);
verts[1] = vec2f(pos.botRight.x, pos.topLeft.y);
verts[3] = vec2f(pos.topLeft.x, pos.botRight.y);
verts[2] = vec2f(pos.botRight.x, pos.botRight.y);
texcoords[0] = vec2f(source.topLeft.x, source.topLeft.y);
texcoords[1] = vec2f(source.botRight.x, source.topLeft.y);
texcoords[3] = vec2f(source.topLeft.x, source.botRight.y);
texcoords[2] = vec2f(source.botRight.x, source.botRight.y);
return true;
}
class FontFT2 : public Font {
public:
std::vector<render::RenderState*> textures;
struct Glyph {
unsigned short x : 16, y : 16;
bool draw : 1;
unsigned char page : 7;
unsigned char w : 8, h : 8;
char wOff : 8, hOff : 8;
float xAdv;
int glyph_index;
};
struct GlyphEntry {
fontChar letter;
Glyph glyph;
bool operator<(const GlyphEntry& other) const { return letter < other.letter; }
GlyphEntry(fontChar Letter) : letter(Letter) {}
GlyphEntry(fontChar Letter, Glyph& Glyph) : letter(Letter), glyph(Glyph) {}
};
Glyph* lowChars;
FT_Face* face;
FT_Size size;
std::vector<GlyphEntry> hiChars;
double x_scale, y_scale;
unsigned glyphHeight, baseLine, textureHeight, textureWidth;
void draw(render::RenderDriver* driver, const char* text, int X, int Y, const Color* color, const recti* clip = 0) const {
float x = (float)X, y = (float)Y;
Color col = color ? *color : Color();
float xFactor = 1.f / float(textureWidth), yFactor = 1.f / float(textureHeight);
vec2f font_verts[4], font_tc[4];
rectf fclip;
if(clip)
fclip = rectf(*clip);
FT_Activate_Size(size);
unsigned lastTexture = (unsigned)textures.size();
VertexBufferTCV* buffer = 0;
u8it it(text);
fontChar lastC = 0;
while(fontChar c = it++) {
if(c == L'\n') {
y += glyphHeight;
x = (float)X;
lastC = 0;
}
else if(c == L'\t') {
x += 32.f - fmod(x - X, 32.f);
lastC = L' ';
}
else {
Glyph glyph;
if(c <= 0xff)
glyph = lowChars[c];
else {
auto g = std::lower_bound(hiChars.begin(), hiChars.end(), c);
if(g != hiChars.end() && g->letter == c) {
glyph = g->glyph;
}
else {
glyph = lowChars['?'];
}
}
//Don't kern anything involving digits
if(c >= '0' && c <= '9') {
lastC = 0;
}
else {
if(lastC) {
FT_Vector kerning;
FT_Get_Kerning(*face, lastC, glyph.glyph_index, 0/*FT_KERNING_UNFITTED*/, &kerning);
if(kerning.x != 0)
x += ((float)kerning.x) / 64.f;
}
lastC = glyph.glyph_index;
}
if(glyph.draw) {
rectf pos = rectf::area(vec2f(x + (float)glyph.wOff, y + (float)glyph.hOff), vec2f(glyph.w, glyph.h));
rectf source = rectf::area(vec2f((float)glyph.x * xFactor, (float)glyph.y * yFactor),
vec2f((float)glyph.w * xFactor, (float)glyph.h * yFactor));
if(clipQuad(pos, source, clip ? &fclip : 0, font_verts, font_tc)) {
if(glyph.page != lastTexture) {
auto& mat = *textures[glyph.page];
buffer = VertexBufferTCV::fetch(&mat);
lastTexture = glyph.page;
}
auto* verts = buffer->request(1, PT_Quads);
for(unsigned i = 0; i < 4; ++i) {
auto& v = verts[i];
v.uv = font_tc[i];
v.col = col;
v.pos.set(font_verts[i].x, font_verts[i].y, 0);
}
}
}
x += glyph.xAdv;
}
}
}
vec2i drawChar(render::RenderDriver* driver, int c, int lastC,
int X, int Y, const Color* color, const recti* clip = 0) const {
float x = (float)X, y = (float)Y;
Color colors[4];
if(color)
colors[0] = colors[1] = colors[2] = colors[3] = *color;
rectf fclip;
if(clip)
fclip = rectf(*clip);
float xFactor = 1.f / float(textureWidth), yFactor = 1.f / float(textureHeight);
vec2f font_verts[4], font_tc[4];
FT_Activate_Size(size);
Glyph glyph;
if(c <= 0xff)
glyph = lowChars[c];
else {
auto g = std::lower_bound(hiChars.begin(), hiChars.end(), c);
if(g != hiChars.end() && g->letter == c) {
glyph = g->glyph;
}
else {
glyph = lowChars['?'];
}
}
//Don't kern anything involving digits
if(c >= '0' && c <= '9') {
lastC = 0;
}
else {
if(lastC) {
FT_Vector kerning;
FT_Get_Kerning(*face, lastC, glyph.glyph_index, 0, &kerning);
if(kerning.x != 0)
x += ((float)kerning.x) / 64.f;
}
lastC = glyph.glyph_index;
}
if(glyph.draw) {
rectf pos = rectf::area(vec2f(x + glyph.wOff, y + glyph.hOff), vec2f(glyph.w, glyph.h));
rectf source = rectf::area(vec2f((float)glyph.x * xFactor, (float)glyph.y * yFactor),
vec2f((float)glyph.w * xFactor, (float)glyph.h * yFactor));
if(clipQuad(pos, source, clip ? &fclip : 0, font_verts, font_tc))
driver->drawQuad(textures[glyph.page], font_verts, font_tc, color ? colors : 0);
}
x += glyph.xAdv;
return vec2i((int)x - X, (int)y - Y);
}
vec2i getDimension(const char* text) const {
vec2i dim(0, glyphHeight);
FT_Activate_Size(size);
fontChar lastC = 0;
u8it it(text);
unsigned maxWidth = 0;
while(fontChar c = it++) {
const Glyph* glyph = 0;
if(c == L'\n') {
if(dim.x > (int)maxWidth)
maxWidth = dim.x;
dim.x = 0;
dim.y += glyphHeight;
}
else if(c <= 0xff) {
glyph = &lowChars[c];
}
else {
auto g = std::lower_bound(hiChars.begin(), hiChars.end(), c);
if(g != hiChars.end() && g->letter == c)
glyph = &g->glyph;
else
glyph = &lowChars['?'];
}
if(glyph) {
dim.x += (int)glyph->xAdv;
//Don't kern anything involving digits
if(c < '0' || c > '9') {
if(lastC) {
FT_Vector kerning;
FT_Get_Kerning(*face, lastC, glyph->glyph_index, 0, &kerning);
if(kerning.x != 0)
dim.x += (unsigned)(kerning.x >> 6);
}
}
}
}
if(dim.x < (int)maxWidth)
dim.x = maxWidth;
return dim;
}
vec2i getDimension(int c, int lastC) const {
vec2i dim(0, glyphHeight);
FT_Activate_Size(size);
const Glyph* glyph = 0;
if(c <= 0xff) {
glyph = &lowChars[c];
}
else {
auto g = std::lower_bound(hiChars.begin(), hiChars.end(), c);
if(g != hiChars.end() && g->letter == c)
glyph = &g->glyph;
else
glyph = &lowChars['?'];
}
if(glyph) {
dim.x += (int)glyph->xAdv;
//Don't kern anything involving digits
if(c < '0' || c > '9') {
if(lastC) {
FT_Vector kerning;
FT_Get_Kerning(*face, lastC, glyph->glyph_index, 0, &kerning);
if(kerning.x != 0)
dim.x += (unsigned)(kerning.x >> 6);
}
}
}
return dim;
}
unsigned getBaseline() const {
return baseLine;
}
unsigned getLineHeight() const {
return glyphHeight;
}
FontFT2()
: lowChars(new Glyph[256]), glyphHeight(0), baseLine(0), textureHeight(1), textureWidth(1)
{
memset(lowChars, 0, sizeof(Glyph) * 256);
}
~FontFT2()
{
FT_Done_Size(size);
delete[] lowChars;
for(auto tex = textures.begin(), end = textures.end(); tex != end; ++tex)
delete *tex;
}
};
FT_Library* library = 0;
std::map<std::string, FT_Face*> faces;
-const int PAGE_SIZE = 512;
+const int FONT_PAGE_SIZE = 512;
Font* loadFontFT2(render::RenderDriver& driver, const char* filename,
std::vector<std::pair<int,int>>& pages, int size) {
FontFT2* font = new FontFT2();
- font->textureWidth = PAGE_SIZE;
- font->textureHeight = PAGE_SIZE;
+ font->textureWidth = FONT_PAGE_SIZE;
+ font->textureHeight = FONT_PAGE_SIZE;
std::vector<Image*> images;
int cur_page = 0;
- Image* img = new Image(PAGE_SIZE, PAGE_SIZE, FMT_Alpha, 0xff);
+ Image* img = new Image(FONT_PAGE_SIZE, FONT_PAGE_SIZE, FMT_Alpha, 0xff);
images.push_back(img);
int x = 0, y = 0;
//Create the library if none exists
if(!library) {
library = new FT_Library();
FT_Init_FreeType(library);
}
//Read in the font face if necessary
FT_Face* face = 0;
auto it = faces.find(filename);
if(it != faces.end()) {
face = it->second;
}
else {
face = new FT_Face();
if (int err = FT_New_Face(*library, filename, 0, face)) {
fprintf(stderr, "Error loading font %s (code %d).\n", filename, err);
return 0;
}
faces[filename] = face;
}
FT_New_Size(*face, &font->size);
FT_Activate_Size(font->size);
FT_Set_Char_Size(*face, 0, size << 6, 96, 96);
font->x_scale = (double)((*face)->size->metrics.x_scale / 65536.0);
font->y_scale = (double)((*face)->size->metrics.y_scale / 65536.0);
font->glyphHeight = (unsigned)((*face)->size->metrics.height >> 6);
font->face = face;
font->baseLine = font->glyphHeight + (unsigned)((*face)->size->metrics.descender >> 6);
int lineHeight = 0;
for(auto page = pages.begin(), end = pages.end(); page != end; ++page) {
int from = page->first;
int to = page->second;
for(int ch = from; ch < to; ++ch) {
int glyph_ind = FT_Get_Char_Index(*face, ch);
FontFT2::Glyph glyph;
glyph.page = cur_page;
if(glyph_ind != 0) {
FT_Load_Glyph(*face, glyph_ind, FT_LOAD_RENDER);
auto glp = (*face)->glyph;
auto bmp = glp->bitmap;
//Collect data about glyph
glyph.draw = true;
glyph.glyph_index = glyph_ind;
glyph.w = glp->bitmap.width;
glyph.h = glp->bitmap.rows;
glyph.wOff = glp->bitmap_left;
glyph.hOff = font->baseLine - glp->bitmap_top;
glyph.xAdv = ((float)glp->metrics.horiAdvance) / 64.f;
glyph.x = x;
glyph.y = y;
if(glyph.h > lineHeight)
lineHeight = glyph.h;
//Find a fitting spot on the image
x += (unsigned)glyph.w + 2;
- if(x > PAGE_SIZE) {
+ if(x > FONT_PAGE_SIZE) {
x = (unsigned)glyph.w + 2;
glyph.x = 0;
y += lineHeight + 1;
glyph.y += lineHeight + 1;
lineHeight = glyph.h;
- if(y + font->glyphHeight + 1 > (unsigned)PAGE_SIZE) {
- img = new Image(PAGE_SIZE, PAGE_SIZE, FMT_Alpha, 0xff);
+ if(y + font->glyphHeight + 1 > (unsigned)FONT_PAGE_SIZE) {
+ img = new Image(FONT_PAGE_SIZE, FONT_PAGE_SIZE, FMT_Alpha, 0xff);
images.push_back(img);
y = 0;
glyph.y = 0;
++cur_page;
++glyph.page;
}
}
//Put the glyph on the actual image
unsigned char* buffer = bmp.buffer;
for(int row = 0; row < bmp.rows; ++row, buffer += bmp.width)
memcpy(&img->get_alpha(glyph.x, glyph.y + row), buffer, bmp.width);
}
else {
glyph.draw = false;
glyph.xAdv = 0;
}
if(ch <= 0xff)
font->lowChars[ch] = glyph;
else
font->hiChars.push_back(FontFT2::GlyphEntry(ch, glyph));
}
}
//Create the textures
for(auto it = images.begin(), end = images.end(); it != end; ++it) {
auto material = new render::RenderState();
material->depthTest = render::DT_NoDepthTest;
material->lighting = false;
material->culling = render::FC_None;
material->baseMat = MAT_Font;
material->depthWrite = false;
if(load_resources)
material->textures[0] = resource::queueImage(*it, 20, true, false);
font->textures.push_back(material);
}
std::sort(font->hiChars.begin(), font->hiChars.end());
return font;
}
};
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Fri, May 15, 8:46 AM (12 h, 46 m ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
63726
Default Alt Text
(12 KB)
Attached To
Mode
R80 StarRuler2-Source
Attached
Detach File
Event Timeline