我正在尝试创建一个自定义C ++库,其中包含FreeType2字体呈现的包装类。这是包装类的头文件:
#ifndef FREETYPEFONT_HPP
#define FREETYPEFONT_HPP
#include <string>
#include <ft2build.h>
#include FT_FREETYPE_H
#include <GL/glew.h>
namespace g4tk
{
struct GlyphData
{
float ax;
float ay;
float left;
float top;
float width;
float rows;
float tx;
};
class FreeTypeFont
{
public:
void Initialize(std::string font_file, int font_size);
void RenderText(std::string str, float x, float y, float z, float sx, float sy);
void RenderInteger(int number, float x, float y, float z, float sx, float sy);
void RenderCurrency(float amount, float x, float y, float z, float sx, float sy);
float CalcWidth(std::string str, float sx);
float CalcAveHeight(std::string str, float sy);
private:
// FreeType-specific variables
FT_Library library;
FT_Face face;
FT_Bool use_kerning;
FT_UInt previous;
// Glyph and atlas variables
int atlas_width;
int atlas_height;
GlyphData Glyphs[128];
// OpenGL variables
GLuint atlas_texture;
GLuint glyph_vao, glyph_vbo[2];
};
}
#endif
主文件:
#include "FreeTypeFont.hpp"
#include <iostream>
#include <sstream>
#include <vector>
#include <cmath>
#include <GL/glew.h>
namespace g4tk
{
void FreeTypeFont::Initialize(std::string font_file, int font_size)
{
// Initialize FreeType library and load face
int error = FT_Init_FreeType(&library);
if(error)
{
std::cout << "An error occurred during library initialization!\n";
}
error = FT_New_Face(library, font_file.c_str(), 0, &face);
if(error == FT_Err_Unknown_File_Format)
{
std::cout << "The font file could be opened and read, but it appears " <<
"that its font format is unsupported.\n";
}
else if(error)
{
std::cout << "Another error code means that the font file could not be " <<
"opened or read, or that it is broken.\n";
}
else std::cout << "Font file sucessfully loaded!\n";
use_kerning = FT_HAS_KERNING(face);
FT_Set_Pixel_Sizes(face, 0, font_size);
FT_GlyphSlot g = face->glyph;
// Get atlas dimensions
atlas_width = atlas_height = 0;
for(int i = 32; i < 128; i++)
{
if(FT_Load_Char(face, i, FT_LOAD_RENDER))
{
fprintf(stderr, "Loading character %c failed!\n", i);
continue;
}
atlas_width += g->bitmap.width;
atlas_height = std::max(atlas_height, int(g->bitmap.rows));
}
// Initialize texture
glActiveTexture(GL_TEXTURE0);
glGenTextures(1, &atlas_texture);
glBindTexture(GL_TEXTURE_2D, atlas_texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, atlas_width, atlas_height, 0, GL_RED, GL_UNSIGNED_BYTE, 0);
// Load individual glyphs
int x_offset = 0;
FT_Vector delta;
for(int i = 32; i < 128; i++)
{
if(FT_Load_Char(face, i, FT_LOAD_RENDER))
{
fprintf(stderr, "Loading character %c failed!\n", i);
continue;
}
Glyphs[i].ax = g->advance.x >> 6;
Glyphs[i].ay = g->advance.y >> 6;
Glyphs[i].left = g->bitmap_left;
Glyphs[i].top = g->bitmap_top;
Glyphs[i].width = g->bitmap.width;
Glyphs[i].rows = g->bitmap.rows;
Glyphs[i].tx = float(x_offset) / float(atlas_width);
glTexSubImage2D(GL_TEXTURE_2D, 0, x_offset, 0, g->bitmap.width, g->bitmap.rows, GL_RED, GL_UNSIGNED_BYTE, g->bitmap.buffer);
x_offset += Glyphs[i].width;
}
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
GLfloat vertex[] = {0.0f, 0.0f, 0.0f};
GLfloat uv[] = {0.0f, 0.0f};
glGenVertexArrays(1, &glyph_vao);
glBindVertexArray(glyph_vao);
glGenBuffers(2, glyph_vbo);
glBindBuffer(GL_ARRAY_BUFFER, glyph_vbo[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertex), vertex, GL_DYNAMIC_DRAW);
GLuint VertexAttributeID = 0;
glVertexAttribPointer(VertexAttributeID, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(VertexAttributeID);
glBindBuffer(GL_ARRAY_BUFFER, glyph_vbo[1]);
glBufferData(GL_ARRAY_BUFFER, sizeof(uv), uv, GL_DYNAMIC_DRAW);
GLuint UvAttributeID = 1;
glVertexAttribPointer(UvAttributeID, 2, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(UvAttributeID);
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindTexture(GL_TEXTURE_2D, 0);
}
void FreeTypeFont::RenderText(std::string str, float x, float y, float z, float sx, float sy)
{
const char * text = str.c_str();
float x2, y2, w, h;
std::vector<GLfloat> vertex, uv;
previous = 0;
FT_Vector delta;
for(const char *p = text; *p; p++)
{
// Retrieve kerning distance and move pen position
if(use_kerning && previous && *p)
{
FT_Get_Kerning(face, previous, *p, 0, &delta);
x += (delta.x >> 6);
}
x2 = x + Glyphs[*p].left * sx;
y2 = -y - Glyphs[*p].top * sy;
w = Glyphs[*p].width * sx;
h = Glyphs[*p].rows * sy;
GLfloat v[] = {
x2, -y2, z,
x2+w, -y2, z,
x2, -y2-h, z,
x2+w, -y2, z,
x2, -y2-h, z,
x2+w, -y2-h, z
};
GLfloat u[] = {
Glyphs[*p].tx, 0.0f,
Glyphs[*p].tx + Glyphs[*p].width/atlas_width, 0.0f,
Glyphs[*p].tx, Glyphs[*p].rows/atlas_height,
Glyphs[*p].tx + Glyphs[*p].width/atlas_width, 0.0f,
Glyphs[*p].tx, Glyphs[*p].rows/atlas_height,
Glyphs[*p].tx + Glyphs[*p].width/atlas_width, Glyphs[*p].rows/atlas_height
};
vertex.insert(vertex.end(), v, v+18);
uv.insert(uv.end(), u, u+12);
x += Glyphs[*p].ax * sx;
y += Glyphs[*p].ay * sy;
previous = *p;
}
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, atlas_texture);
glBindVertexArray(glyph_vao);
glBindBuffer(GL_ARRAY_BUFFER, glyph_vbo[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(float)*vertex.size(), &vertex[0], GL_DYNAMIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, glyph_vbo[1]);
glBufferData(GL_ARRAY_BUFFER, sizeof(float)*uv.size(), &uv[0], GL_DYNAMIC_DRAW);
glDrawArrays(GL_TRIANGLES, 0, 6*strlen(text));
}
void FreeTypeFont::RenderInteger(int number, float x, float y, float z, float sx, float sy)
{
std::ostringstream os;
os << number;
std::string integer = os.str();
RenderText(integer, x, y, z, sx, sy);
}
void FreeTypeFont::RenderCurrency(float amount, float x, float y, float z, float sx, float sy)
{
float rounded_amount = round(100.0f*amount)/100.0f;
std::ostringstream os;
os << '$' << amount;
std::string money = os.str();
/*
while((money.substr(money.find('.'))).length() > 3){
money = money.substr(0, money.length()-1);
}
*/
RenderText(money, x, y, z, sx, sy);
}
float FreeTypeFont::CalcWidth(std::string str, float sx)
{
const char * text = str.c_str();
float width = 0.0;
previous = 0;
FT_Vector delta;
for(const char *p = text; *p; p++)
{
// Retrieve kerning distance and move pen position
if(use_kerning && previous && *p)
{
FT_Get_Kerning(face, previous, *p, 0, &delta);
width += (delta.x >> 6);
}
width += Glyphs[*p].ax * sx;
previous = *p;
}
return width;
}
float FreeTypeFont::CalcAveHeight(std::string str, float sy)
{
const char * text = str.c_str();
float height = 0.0;
for(const char *p = text; *p; p++) height += Glyphs[*p].ay * sy;
height /= float(str.length());
return height;
}
}
自定义库g4tk是使用CMake静态编译的:
# This is the CMakeLists file for the PhotonDemo program.
cmake_minimum_required(VERSION 2.8.9)
if(COMMAND CMAKE_POLICY)
cmake_policy(SET CMP0003 NEW)
endif()
project(g4tk)
# OpenGL
find_package(OpenGL REQUIRED)
if(OPENGL_FOUND)
include_directories(${OPENGL_INCLUDE_DIRS})
link_libraries(${OPENGL_LIBRARIES})
endif()
# GLEW
find_package(GLEW REQUIRED)
if(GLEW_FOUND)
include_directories(${GLEW_INCLUDE_DIRS})
link_libraries(${GLEW_LIBRARIES})
endif()
# GLFW
find_package(PkgConfig REQUIRED)
pkg_search_module(GLFW REQUIRED glfw3)
include_directories(${GLFW_INCLUDE_DIRS})
# FreeType
find_package(Freetype REQUIRED)
include_directories(${FREETYPE_INCLUDE_DIRS})
# Add sources and headers
set(SOURCE
${SOURCE}
${CMAKE_CURRENT_SOURCE_DIR}/OGLT.cpp
${CMAKE_CURRENT_SOURCE_DIR}/FreeTypeFont.cpp
${CMAKE_CURRENT_SOURCE_DIR}/LayerUI.cpp
${CMAKE_CURRENT_SOURCE_DIR}/UiTabs.cpp
)
set(HEADERS
${HEADERS}
${CMAKE_CURRENT_SOURCE_DIR}/OGLT.hpp
${CMAKE_CURRENT_SOURCE_DIR}/FreeTypeFont.hpp
${CMAKE_CURRENT_SOURCE_DIR}/LayerUI.hpp
${CMAKE_CURRENT_SOURCE_DIR}/UiTabs.hpp
)
add_library(g4tk STATIC ${SOURCE} ${HEADERS})
库编译得很好,我可以在命令行上使用我的库来测试程序,例如:
g++ -g test.cpp -L/home/steven/C++/G4TK/Build -I/home/steven/C++/G4TK/Source -I/usr/include/freetype2 -lg4tk -lGLEW -lglfw3 -lGL -lX11 -lXi -lXrandr -lXxf86vm -lXinerama -lXcursor -lrt -lm -pthread -lfreetype
但是,当我尝试在CMake中使用我的库来构建另一个项目时,我的库没有与FreeType链接。这是新示例项目的CMakeLists.txt:
# This is the CMakeLists file for the DIVS program.
cmake_minimum_required(VERSION 2.8.9)
if(COMMAND CMAKE_POLICY)
cmake_policy(SET CMP0003 NEW)
endif()
project(DIVS)
# Find ITK.
find_package(ITK REQUIRED)
include(${ITK_USE_FILE})
# OpenGL
find_package(OpenGL REQUIRED)
if(OPENGL_FOUND)
include_directories(${OPENGL_INCLUDE_DIRS})
link_libraries(${OPENGL_LIBRARIES})
endif()
# GLEW
find_package(GLEW REQUIRED)
if(GLEW_FOUND)
include_directories(${GLEW_INCLUDE_DIRS})
link_libraries(${GLEW_LIBRARIES})
endif()
# GLFW
find_package(PkgConfig REQUIRED)
pkg_search_module(GLFW REQUIRED glfw3)
include_directories(${GLFW_INCLUDE_DIRS})
# FreeType
find_package(Freetype REQUIRED)
include_directories(${FREETYPE_INCLUDE_DIRS})
include_directories(/home/steven/C++/G4TK/Source/)
link_directories(/home/steven/C++/G4TK/Build/)
# Set compiler flags and linked libraries
SET(GCC_COVERAGE_COMPILE_FLAGS "-g")
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${GCC_COVERAGE_COMPILE_FLAGS}")
# Add sources and headers
set(SOURCE
${SOURCE}
${CMAKE_CURRENT_SOURCE_DIR}/Main.cpp
#/home/steven/C++/G4TK/Source/FreeTypeFont.cpp
)
#set(HEADERS
# ${HEADERS}
#)
#add_executable(DIVS ${SOURCE} ${HEADERS})
add_executable(DIVS ${SOURCE})
target_link_libraries(DIVS ${ITK_LIBRARIES} ${GLEW_LIBRARIES} ${OPENGL_LIBRARIES} ${GLFW_STATIC_LIBRARIES} ${FREETYPE_LIBRARIES} -lg4tk)
还有一些示例错误:
FreeTypeFont.cpp:(.text+0x1c): undefined reference to 'FT_Init_FreeType'
FreeTypeFont.cpp:(.text+0x62): undefined reference to 'FT_New_Face'
FreeTypeFont.cpp:(.text+0xec): undefined reference to 'FT_Set_Pixel_Sizes'
FreeTypeFont.cpp:(.text+0x142): undefined reference to 'FT_Load_Char'
FreeTypeFont.cpp:(.text+0x273): undefined reference to 'FT_Load_Char'
所以我的FreeTypeFont类没有正确链接。但是,如果我取消评论“/home/steven/C++/G4TK/Source/FreeTypeFont.cpp”行,项目编译就好了。有没有办法让我的库编译而不包括CMakeLists.txt中的源代码文件?我使用GCC在Ubuntu 14.04上。