#include "texture_atlas.h"
#include "texture_font.h"
+#include "freetype_library.h"
#include <map>
#include <memory>
namespace caspar { namespace core { namespace text {
-struct freetype_exception : virtual caspar_exception
-{
- freetype_exception() {}
- explicit freetype_exception(const char* msg) : caspar_exception(msg) {}
-};
-
struct unicode_range
{
unicode_range() : first(0), last(0) {}
private:
struct glyph_info
{
- glyph_info(int w, int h, float l, float t, float r, float b) : width(w), height(h), left(l), top(t), right(r), bottom(b)
+ glyph_info(int w, int h, double l, double t, double r, double b) : width(w), height(h), left(l), top(t), right(r), bottom(b)
{}
- float left, top, right, bottom;
+ double left, top, right, bottom;
int width, height;
};
- std::shared_ptr<FT_LibraryRec_> lib_;
- std::shared_ptr<FT_FaceRec_> face_;
+ spl::shared_ptr<FT_FaceRec_> face_;
texture_atlas atlas_;
- float size_;
- float tracking_;
+ double size_;
+ double tracking_;
bool normalize_;
std::map<int, glyph_info> glyphs_;
+ std::wstring name_;
public:
- impl(texture_atlas& atlas, const text_info& info, bool normalize_coordinates) : atlas_(atlas), size_(info.size), tracking_(info.size*info.tracking/1000.0f), normalize_(normalize_coordinates)
+ impl(texture_atlas& atlas, const text_info& info, bool normalize_coordinates)
+ : face_(get_new_face(u8(info.font_file), u8(info.font)))
+ , atlas_(atlas)
+ , size_(info.size)
+ , tracking_(info.size*info.tracking/1000.0)
+ , normalize_(normalize_coordinates)
+ , name_(info.font)
{
- FT_Library lib;
-
- if (FT_Init_FreeType(&lib))
- throw freetype_exception("Failed to initialize freetype");
-
- lib_.reset(lib, [](FT_Library ptr) { FT_Done_FreeType(ptr); });
-
- FT_Face face;
-
- if (FT_New_Face(lib_.get(), u8(info.font_file).c_str(), 0, &face))
- throw freetype_exception("Failed to load font");
-
- face_.reset(face, [](FT_Face ptr) { FT_Done_Face(ptr); });
-
if (FT_Set_Char_Size(face_.get(), static_cast<FT_F26Dot6>(size_*64), 0, 72, 72))
- throw freetype_exception("Failed to set font size");
+ CASPAR_THROW_EXCEPTION(expected_freetype_exception() << msg_info("Failed to set font size"));
}
- void set_tracking(int tracking)
+ void set_tracking(double tracking)
{
- tracking_ = size_ * tracking / 1000.0f;
+ tracking_ = size_ * tracking / 1000.0;
}
int count_glyphs_in_range(unicode_block block)
- {
+ {
unicode_range range = get_range(block);
//TODO: extract info from freetype
return range.last - range.first;
}
- void load_glyphs(unicode_block block, const color<float>& col)
+ void load_glyphs(unicode_block block, const color<double>& col)
{
- FT_Error err;
int flags = FT_LOAD_RENDER | FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_NORMAL;
unicode_range range = get_range(block);
FT_UInt glyph_index = FT_Get_Char_Index(face_.get(), i);
if(!glyph_index) //ignore codes that doesn't have a glyph for now. Might want to map these to a special glyph later.
continue;
-
- err = FT_Load_Glyph(face_.get(), glyph_index, flags);
+
+ FT_Error err = FT_Load_Glyph(face_.get(), glyph_index, flags);
if(err) continue; //igonore glyphs that fail to load
const FT_Bitmap& bitmap = face_->glyph->bitmap; //shorthand notation
}
atlas_.set_region(region.x, region.y, bitmap.width, bitmap.rows, bitmap.buffer, bitmap.pitch, col);
- glyphs_.insert(std::pair<int, glyph_info>(i, glyph_info(bitmap.width, bitmap.rows,
- region.x / (float)atlas_.width(),
- region.y / (float)atlas_.height(),
- (region.x + bitmap.width) / (float)atlas_.width(),
- (region.y + bitmap.rows) / (float)atlas_.height())));
+ glyphs_.insert(std::pair<int, glyph_info>(i, glyph_info(bitmap.width, bitmap.rows,
+ region.x / static_cast<double>(atlas_.width()),
+ region.y / static_cast<double>(atlas_.height()),
+ (region.x + bitmap.width) / static_cast<double>(atlas_.width()),
+ (region.y + bitmap.rows) / static_cast<double>(atlas_.height()))));
}
}
- std::vector<float> create_vertex_stream(const std::wstring& str, int x, int y, int parent_width, int parent_height, string_metrics* metrics)
+ std::vector<frame_geometry::coord> create_vertex_stream(const std::wstring& str, int x, int y, int parent_width, int parent_height, string_metrics* metrics, double shear)
{
//TODO: detect glyphs that aren't in the atlas and load them (and maybe that entire unicode_block on the fly
- std::vector<float> result(16*str.length(), 0);
+ std::vector<frame_geometry::coord> result;
+ result.resize(4 * str.length());
bool use_kerning = (face_->face_flags & FT_FACE_FLAG_KERNING) == FT_FACE_FLAG_KERNING;
int index = 0;
FT_UInt previous = 0;
- float pos_x = (float)x;
- float pos_y = (float)y;
+ double pos_x = static_cast<double>(x);
+ double pos_y = static_cast<double>(y);
int maxBearingY = 0;
int maxProtrudeUnderY = 0;
int maxHeight = 0;
- auto end = str.end();
- for(auto it = str.begin(); it != end; ++it, ++index)
+ for (auto it = str.begin(), end = str.end(); it != end; ++it, ++index)
{
auto glyph_it = glyphs_.find(*it);
- if(glyph_it != glyphs_.end())
- {
+ if (glyph_it != glyphs_.end())
+ {
const glyph_info& coords = glyph_it->second;
FT_UInt glyph_index = FT_Get_Char_Index(face_.get(), (*it));
FT_Vector delta;
FT_Get_Kerning(face_.get(), previous, glyph_index, FT_KERNING_DEFAULT, &delta);
- pos_x += delta.x / 64.0f;
+ pos_x += delta.x / 64.0;
}
FT_Load_Glyph(face_.get(), glyph_index, FT_LOAD_NO_BITMAP | FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_NORMAL);
- float left = (pos_x + face_->glyph->metrics.horiBearingX/64.0f) / parent_width ;
- float right = ((pos_x + face_->glyph->metrics.horiBearingX/64.0f) + coords.width) / parent_width;
-
- float top = (pos_y - face_->glyph->metrics.horiBearingY/64.0f) / parent_height;
- float bottom = ((pos_y - face_->glyph->metrics.horiBearingY/64.0f) + coords.height) / parent_height;
-
- //vertex 1 top left
- result[index*16 + 0] = left; //vertex.x
- result[index*16 + 1] = top; //vertex.y
- result[index*16 + 2] = coords.left; //texcoord.r
- result[index*16 + 3] = coords.top; //texcoord.s
-
- //vertex 2 top right
- result[index*16 + 4] = right; //vertex.x
- result[index*16 + 5] = top; //vertex.y
- result[index*16 + 6] = coords.right; //texcoord.r
- result[index*16 + 7] = coords.top; //texcoord.s
-
- //vertex 3 bottom right
- result[index*16 + 8] = right; //vertex.x
- result[index*16 + 9] = bottom; //vertex.y
- result[index*16 + 10] = coords.right; //texcoord.r
- result[index*16 + 11] = coords.bottom; //texcoord.s
-
- //vertex 4 bottom left
- result[index*16 + 12] = left; //vertex.x
- result[index*16 + 13] = bottom; //vertex.y
- result[index*16 + 14] = coords.left; //texcoord.r
- result[index*16 + 15] = coords.bottom; //texcoord.s
+ double left = (pos_x + face_->glyph->metrics.horiBearingX / 64.0) / parent_width;
+ double right = ((pos_x + face_->glyph->metrics.horiBearingX / 64.0) + coords.width) / parent_width;
+
+ double top = (pos_y - face_->glyph->metrics.horiBearingY/64.0) / parent_height;
+ double bottom = ((pos_y - face_->glyph->metrics.horiBearingY / 64.0) + coords.height) / parent_height;
+
+ auto ul_index = index * 4;
+ auto ur_index = ul_index + 1;
+ auto lr_index = ul_index + 2;
+ auto ll_index = ul_index + 3;
+
+ //vertex 1 upper left
+ result[ul_index].vertex_x = left; //vertex.x
+ result[ul_index].vertex_y = top; //vertex.y
+ result[ul_index].texture_x = coords.left; //texcoord.r
+ result[ul_index].texture_y = coords.top; //texcoord.s
+
+ //vertex 2 upper right
+ result[ur_index].vertex_x = right; //vertex.x
+ result[ur_index].vertex_y = top; //vertex.y
+ result[ur_index].texture_x = coords.right; //texcoord.r
+ result[ur_index].texture_y = coords.top; //texcoord.s
+
+ //vertex 3 lower right
+ result[lr_index].vertex_x = right; //vertex.x
+ result[lr_index].vertex_y = bottom; //vertex.y
+ result[lr_index].texture_x = coords.right; //texcoord.r
+ result[lr_index].texture_y = coords.bottom; //texcoord.s
+
+ //vertex 4 lower left
+ result[ll_index].vertex_x = left; //vertex.x
+ result[ll_index].vertex_y = bottom; //vertex.y
+ result[ll_index].texture_x = coords.left; //texcoord.r
+ result[ll_index].texture_y = coords.bottom; //texcoord.s
int bearingY = face_->glyph->metrics.horiBearingY >> 6;
if (maxBearingY + maxProtrudeUnderY > maxHeight)
maxHeight = maxBearingY + maxProtrudeUnderY;
- pos_x += face_->glyph->advance.x / 64.0f;
+ pos_x += face_->glyph->advance.x / 64.0;
pos_x += tracking_;
previous = glyph_index;
}
if(normalize_)
{
- float ratio_x = parent_width/(pos_x - x);
- float ratio_y = parent_height/(float)(maxHeight);
- for(index = 0; index < result.size(); index += 4)
+ auto ratio_x = parent_width / (pos_x - x);
+ auto ratio_y = parent_height / static_cast<double>(maxHeight);
+
+ for (auto& coord : result)
{
- result[index + 0] *= ratio_x;
- result[index + 1] *= ratio_y;
+ coord.vertex_x *= ratio_x;
+ coord.vertex_y *= ratio_y;
}
}
- if(metrics != nullptr)
+ if (metrics != nullptr)
{
- metrics->width = (int)(pos_x - x + 0.5f);
+ metrics->width = (int)(pos_x - x + 0.5);
metrics->bearingY = maxBearingY;
metrics->height = maxHeight;
metrics->protrudeUnderY = maxProtrudeUnderY;
return result;
}
- string_metrics measure_string(const std::wstring& str)
+ std::wstring get_name() const
{
- string_metrics result;
-
- bool use_kerning = (face_->face_flags & FT_FACE_FLAG_KERNING) == FT_FACE_FLAG_KERNING;
- int index = 0;
- FT_UInt previous = 0;
- float pos_x = 0;
-// float pos_y = 0;
-
- auto end = str.end();
- for(auto it = str.begin(); it != end; ++it, ++index)
- {
- auto glyph_it = glyphs_.find(*it);
- if(glyph_it != glyphs_.end())
- {
- const glyph_info& coords = glyph_it->second;
-
- FT_UInt glyph_index = FT_Get_Char_Index(face_.get(), (*it));
-
- if(use_kerning && previous && glyph_index)
- {
- FT_Vector delta;
- FT_Get_Kerning(face_.get(), previous, glyph_index, FT_KERNING_DEFAULT, &delta);
-
- pos_x += delta.x / 64.0f;
- }
-
- FT_Load_Glyph(face_.get(), glyph_index, FT_LOAD_NO_BITMAP | FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_NORMAL);
-
- int bearingY = face_->glyph->metrics.horiBearingY >> 6;
- if(bearingY > result.bearingY)
- result.bearingY = bearingY;
-
- int protrudeUnderY = coords.height - bearingY;
-
- if (protrudeUnderY > result.protrudeUnderY)
- result.protrudeUnderY = protrudeUnderY;
-
- if (result.bearingY + result.protrudeUnderY > result.height)
- result.height = result.bearingY + result.protrudeUnderY;
-
- pos_x += face_->glyph->advance.x / 64.0f;
- previous = glyph_index;
- }
- }
+ return name_;
+ }
- result.width = (int)(pos_x+.5f);
- return result;
+ double get_size() const
+ {
+ return size_;
}
-};
+};
texture_font::texture_font(texture_atlas& atlas, const text_info& info, bool normalize_coordinates) : impl_(new impl(atlas, info, normalize_coordinates)) {}
-void texture_font::load_glyphs(unicode_block range, const color<float>& col) { impl_->load_glyphs(range, col); }
-void texture_font::set_tracking(int tracking) { impl_->set_tracking(tracking); }
-std::vector<float> texture_font::create_vertex_stream(const std::wstring& str, int x, int y, int parent_width, int parent_height, string_metrics* metrics) { return impl_->create_vertex_stream(str, x, y, parent_width, parent_height, metrics); }
-string_metrics texture_font::measure_string(const std::wstring& str) { return impl_->measure_string(str); }
+void texture_font::load_glyphs(unicode_block range, const color<double>& col) { impl_->load_glyphs(range, col); }
+void texture_font::set_tracking(double tracking) { impl_->set_tracking(tracking); }
+std::vector<frame_geometry::coord> texture_font::create_vertex_stream(const std::wstring& str, int x, int y, int parent_width, int parent_height, string_metrics* metrics, double shear) { return impl_->create_vertex_stream(str, x, y, parent_width, parent_height, metrics, shear); }
+std::wstring texture_font::get_name() const { return impl_->get_name(); }
+double texture_font::get_size() const { return impl_->get_size(); }
unicode_range get_range(unicode_block block)
{