CMake — пользовательская библиотека Freetype не связывается

Я пытаюсь создать пользовательскую библиотеку 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? Я использую Ubuntu 14.04, используя GCC.


person stevend12    schedule 28.10.2017    source источник
comment
Попробуйте переместить -lg4tk на первое место в вызове target_link_libraries. А -l лишнее.   -  person arrowd    schedule 28.10.2017
comment
Спасибо, это сработало! Ты знаешь почему?   -  person stevend12    schedule 28.10.2017
comment
stackoverflow.com/a/35838211/637669   -  person arrowd    schedule 28.10.2017
comment
Спасибо, думаю, мне есть что узнать о компиляторах и библиотеках. Я полагал, что g4tk, который зависит от FreeType, сначала потребует Freetype, но я посмотрю на это. Если вы хотите получить кредит на ответ, вы можете добавить его ниже, в противном случае я скоро.   -  person stevend12    schedule 28.10.2017