+ use FT_Stroker to stroke glyphs outlines
(we can have outlined text now!) + allow fractional halo_radius + TODO: Rename halo_fill to stroke halo_radius to stroke-width + TODO: Implement proper 'halo' effect
This commit is contained in:
parent
f0d579f742
commit
e645338237
8 changed files with 116 additions and 47 deletions
|
@ -38,6 +38,7 @@ extern "C"
|
|||
#include <ft2build.h>
|
||||
#include FT_FREETYPE_H
|
||||
#include FT_GLYPH_H
|
||||
#include FT_STROKER_H
|
||||
}
|
||||
|
||||
// boost
|
||||
|
@ -303,7 +304,41 @@ private:
|
|||
std::vector<face_ptr> faces_;
|
||||
};
|
||||
|
||||
// FT_Stroker wrapper
|
||||
class stroker : boost::noncopyable
|
||||
{
|
||||
public:
|
||||
explicit stroker(FT_Stroker s)
|
||||
: s_(s) {}
|
||||
|
||||
void init(double radius)
|
||||
{
|
||||
FT_Stroker_Set(s_,radius * (1<<6),
|
||||
FT_STROKER_LINECAP_ROUND,
|
||||
FT_STROKER_LINEJOIN_ROUND,
|
||||
0);
|
||||
}
|
||||
|
||||
FT_Stroker const& get() const
|
||||
{
|
||||
return s_;
|
||||
}
|
||||
|
||||
~stroker()
|
||||
{
|
||||
#ifdef MAPNIK_DEBUG
|
||||
std::clog << "~stroker: destroy stroker:" << s_ << std::endl;
|
||||
#endif
|
||||
FT_Stroker_Done(s_);
|
||||
}
|
||||
private:
|
||||
FT_Stroker s_;
|
||||
};
|
||||
|
||||
|
||||
|
||||
typedef boost::shared_ptr<font_face_set> face_set_ptr;
|
||||
typedef boost::shared_ptr<stroker> stroker_ptr;
|
||||
|
||||
class MAPNIK_DECL freetype_engine
|
||||
{
|
||||
|
@ -311,6 +346,7 @@ public:
|
|||
static bool register_font(std::string const& file_name);
|
||||
static std::vector<std::string> face_names ();
|
||||
face_ptr create_face(std::string const& family_name);
|
||||
stroker_ptr create_stroker();
|
||||
virtual ~freetype_engine();
|
||||
freetype_engine();
|
||||
private:
|
||||
|
@ -327,7 +363,8 @@ class MAPNIK_DECL face_manager : private boost::noncopyable
|
|||
|
||||
public:
|
||||
face_manager(T & engine)
|
||||
: engine_(engine) {}
|
||||
: engine_(engine),
|
||||
stroker_(engine_.create_stroker()) {}
|
||||
|
||||
face_ptr get_face(std::string const& name)
|
||||
{
|
||||
|
@ -371,9 +408,16 @@ public:
|
|||
}
|
||||
return face_set;
|
||||
}
|
||||
|
||||
stroker_ptr get_stroker()
|
||||
{
|
||||
return stroker_;
|
||||
}
|
||||
|
||||
private:
|
||||
faces faces_;
|
||||
font_engine_type & engine_;
|
||||
stroker_ptr stroker_;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
|
@ -389,12 +433,13 @@ struct text_renderer : private boost::noncopyable
|
|||
typedef boost::ptr_vector<glyph_t> glyphs_t;
|
||||
typedef T pixmap_type;
|
||||
|
||||
text_renderer (pixmap_type & pixmap, face_set_ptr faces)
|
||||
text_renderer (pixmap_type & pixmap, face_set_ptr faces, stroker & s)
|
||||
: pixmap_(pixmap),
|
||||
faces_(faces),
|
||||
stroker_(s),
|
||||
fill_(0,0,0),
|
||||
halo_fill_(255,255,255),
|
||||
halo_radius_(0),
|
||||
halo_radius_(0.0),
|
||||
opacity_(1.0) {}
|
||||
|
||||
void set_pixel_size(unsigned size)
|
||||
|
@ -412,7 +457,7 @@ struct text_renderer : private boost::noncopyable
|
|||
halo_fill_=halo;
|
||||
}
|
||||
|
||||
void set_halo_radius( int radius=1)
|
||||
void set_halo_radius( double radius=1.0)
|
||||
{
|
||||
halo_radius_=radius;
|
||||
}
|
||||
|
@ -511,23 +556,28 @@ struct text_renderer : private boost::noncopyable
|
|||
typename glyphs_t::iterator pos;
|
||||
|
||||
//make sure we've got reasonable values.
|
||||
if (halo_radius_ > 0 && halo_radius_ < 256)
|
||||
if (halo_radius_ > 0.0 && halo_radius_ < 1024.0)
|
||||
{
|
||||
//render halo
|
||||
stroker_.init(halo_radius_);
|
||||
for ( pos = glyphs_.begin(); pos != glyphs_.end();++pos)
|
||||
{
|
||||
FT_Glyph_Transform(pos->image,0,&start);
|
||||
|
||||
error = FT_Glyph_To_Bitmap( &(pos->image),FT_RENDER_MODE_NORMAL,0,1);
|
||||
if ( ! error )
|
||||
FT_Glyph g;
|
||||
error = FT_Glyph_Copy(pos->image, &g);
|
||||
if (!error)
|
||||
{
|
||||
|
||||
FT_BitmapGlyph bit = (FT_BitmapGlyph)pos->image;
|
||||
render_halo(&bit->bitmap, halo_fill_.rgba(),
|
||||
bit->left,
|
||||
height - bit->top,halo_radius_);
|
||||
FT_Glyph_Transform(g,0,&start);
|
||||
FT_Glyph_Stroke(&g,stroker_.get(),0);
|
||||
error = FT_Glyph_To_Bitmap( &g,FT_RENDER_MODE_NORMAL,0,1);
|
||||
if ( ! error )
|
||||
{
|
||||
|
||||
FT_BitmapGlyph bit = (FT_BitmapGlyph)g;
|
||||
render_bitmap(&bit->bitmap, halo_fill_.rgba(),
|
||||
bit->left,
|
||||
height - bit->top);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//render actual text
|
||||
for ( pos = glyphs_.begin(); pos != glyphs_.end();++pos)
|
||||
|
@ -591,11 +641,10 @@ private:
|
|||
|
||||
pixmap_type & pixmap_;
|
||||
face_set_ptr faces_;
|
||||
stroker & stroker_;
|
||||
color fill_;
|
||||
color halo_fill_;
|
||||
int halo_radius_;
|
||||
unsigned text_ratio_;
|
||||
unsigned wrap_width_;
|
||||
double halo_radius_;
|
||||
glyphs_t glyphs_;
|
||||
double opacity_;
|
||||
};
|
||||
|
|
|
@ -135,8 +135,8 @@ struct MAPNIK_DECL text_symbolizer
|
|||
void set_fill(color const& fill);
|
||||
void set_halo_fill(color const& fill);
|
||||
color const& get_halo_fill() const;
|
||||
void set_halo_radius(unsigned radius);
|
||||
unsigned get_halo_radius() const;
|
||||
void set_halo_radius(double radius);
|
||||
double get_halo_radius() const;
|
||||
void set_label_placement(label_placement_e label_p);
|
||||
label_placement_e get_label_placement() const;
|
||||
void set_vertical_alignment(vertical_alignment_e valign);
|
||||
|
@ -178,7 +178,7 @@ private:
|
|||
double max_char_angle_delta_;
|
||||
color fill_;
|
||||
color halo_fill_;
|
||||
unsigned halo_radius_;
|
||||
double halo_radius_;
|
||||
label_placement_e label_p_;
|
||||
vertical_alignment_e valign_;
|
||||
position anchor_;
|
||||
|
|
|
@ -88,7 +88,7 @@ void agg_renderer<T>::process(markers_symbolizer const& sym,
|
|||
|
||||
path_type path(t_,geom,prj_trans);
|
||||
markers_placement<path_type, label_collision_detector4> placement(path, extent, detector_,
|
||||
sym.get_spacing(),
|
||||
sym.get_spacing() * scale_factor_,
|
||||
sym.get_max_error(),
|
||||
sym.get_allow_overlap());
|
||||
double x, y, angle;
|
||||
|
@ -126,7 +126,7 @@ void agg_renderer<T>::process(markers_symbolizer const& sym,
|
|||
|
||||
path_type path(t_,geom,prj_trans);
|
||||
markers_placement<path_type, label_collision_detector4> placement(path, extent, detector_,
|
||||
sym.get_spacing(),
|
||||
sym.get_spacing() * scale_factor_,
|
||||
sym.get_max_error(),
|
||||
sym.get_allow_overlap());
|
||||
double x, y, angle;
|
||||
|
|
|
@ -117,14 +117,15 @@ void agg_renderer<T>::process(shield_symbolizer const& sym,
|
|||
faces = font_manager_.get_face_set(sym.get_face_name());
|
||||
}
|
||||
|
||||
if (faces->size() > 0)
|
||||
stroker_ptr strk = font_manager_.get_stroker();
|
||||
if (strk && faces->size() > 0)
|
||||
{
|
||||
text_renderer<T> text_ren(pixmap_, faces);
|
||||
text_renderer<T> text_ren(pixmap_, faces, *strk);
|
||||
|
||||
text_ren.set_pixel_size(sym.get_text_size() * scale_factor_);
|
||||
text_ren.set_fill(sym.get_fill());
|
||||
text_ren.set_halo_fill(sym.get_halo_fill());
|
||||
text_ren.set_halo_radius(sym.get_halo_radius());
|
||||
text_ren.set_halo_radius(sym.get_halo_radius() * scale_factor_);
|
||||
text_ren.set_opacity(sym.get_text_opacity());
|
||||
|
||||
placement_finder<label_collision_detector4> finder(detector_);
|
||||
|
@ -255,10 +256,11 @@ void agg_renderer<T>::process(shield_symbolizer const& sym,
|
|||
faces = font_manager_.get_face_set(sym.get_face_name());
|
||||
}
|
||||
|
||||
if (faces->size() > 0)
|
||||
stroker_ptr strk = font_manager_.get_stroker();
|
||||
if (strk && faces->size() > 0)
|
||||
{
|
||||
text_renderer<T> ren(pixmap_, faces);
|
||||
|
||||
text_renderer<T> ren(pixmap_, faces, *strk);
|
||||
|
||||
ren.set_pixel_size(sym.get_text_size());
|
||||
ren.set_fill(sym.get_fill());
|
||||
ren.set_halo_fill(sym.get_halo_fill());
|
||||
|
@ -303,8 +305,12 @@ void agg_renderer<T>::process(shield_symbolizer const& sym,
|
|||
prj_trans.backward(label_x,label_y, z);
|
||||
t_.forward(&label_x,&label_y);
|
||||
|
||||
finder.find_point_placement( text_placement,label_x,label_y,0.0,sym.get_vertical_alignment(),sym.get_line_spacing(),
|
||||
sym.get_character_spacing(),sym.get_horizontal_alignment(),sym.get_justify_alignment() );
|
||||
finder.find_point_placement( text_placement,label_x,label_y,0.0,
|
||||
sym.get_vertical_alignment(),
|
||||
sym.get_line_spacing(),
|
||||
sym.get_character_spacing(),
|
||||
sym.get_horizontal_alignment(),
|
||||
sym.get_justify_alignment() );
|
||||
|
||||
// check to see if image overlaps anything too, there is only ever 1 placement found for points and verticies
|
||||
if( text_placement.placements.size() > 0)
|
||||
|
|
|
@ -642,13 +642,14 @@ void agg_renderer<T>::process(text_symbolizer const& sym,
|
|||
faces = font_manager_.get_face_set(sym.get_face_name());
|
||||
}
|
||||
|
||||
if (faces->size() > 0)
|
||||
stroker_ptr strk = font_manager_.get_stroker();
|
||||
if (faces->size() > 0 && strk)
|
||||
{
|
||||
text_renderer<T> ren(pixmap_, faces);
|
||||
text_renderer<T> ren(pixmap_, faces, *strk);
|
||||
ren.set_pixel_size(sym.get_text_size() * scale_factor_);
|
||||
ren.set_fill(fill);
|
||||
ren.set_halo_fill(sym.get_halo_fill());
|
||||
ren.set_halo_radius(sym.get_halo_radius());
|
||||
ren.set_halo_radius(sym.get_halo_radius() * scale_factor_);
|
||||
ren.set_opacity(sym.get_text_opacity());
|
||||
|
||||
placement_finder<label_collision_detector4> finder(detector_);
|
||||
|
@ -714,7 +715,8 @@ void agg_renderer<T>::process(glyph_symbolizer const& sym,
|
|||
proj_transform const& prj_trans)
|
||||
{
|
||||
face_set_ptr faces = font_manager_.get_face_set(sym.get_face_name());
|
||||
if (faces->size() > 0)
|
||||
stroker_ptr strk = font_manager_.get_stroker();
|
||||
if (faces->size() > 0 && strk)
|
||||
{
|
||||
// Get x and y from geometry and translate to pixmap coords.
|
||||
double x, y, z=0.0;
|
||||
|
@ -722,14 +724,14 @@ void agg_renderer<T>::process(glyph_symbolizer const& sym,
|
|||
prj_trans.backward(x,y,z);
|
||||
t_.forward(&x, &y);
|
||||
|
||||
text_renderer<T> ren(pixmap_, faces);
|
||||
text_renderer<T> ren(pixmap_, faces, *strk);
|
||||
|
||||
// set fill and halo colors
|
||||
color fill = sym.eval_color(feature);
|
||||
ren.set_fill(fill);
|
||||
if (fill != color("transparent")) {
|
||||
ren.set_halo_fill(sym.get_halo_fill());
|
||||
ren.set_halo_radius(sym.get_halo_radius());
|
||||
ren.set_halo_radius(sym.get_halo_radius() * scale_factor_);
|
||||
}
|
||||
|
||||
// set font size
|
||||
|
|
|
@ -92,6 +92,18 @@ face_ptr freetype_engine::create_face(std::string const& family_name)
|
|||
}
|
||||
return face_ptr();
|
||||
}
|
||||
|
||||
stroker_ptr freetype_engine::create_stroker()
|
||||
{
|
||||
FT_Stroker s;
|
||||
FT_Error error = FT_Stroker_New(library_, &s);
|
||||
if (!error)
|
||||
{
|
||||
return stroker_ptr(new stroker(s));
|
||||
}
|
||||
return stroker_ptr();
|
||||
}
|
||||
|
||||
#ifdef MAPNIK_THREADSAFE
|
||||
boost::mutex freetype_engine::mutex_;
|
||||
#endif
|
||||
|
|
|
@ -1020,13 +1020,13 @@ void map_parser::parse_text_symbolizer( rule_type & rule, ptree const & sym )
|
|||
{
|
||||
text_symbol.set_halo_fill( * halo_fill );
|
||||
}
|
||||
optional<unsigned> halo_radius =
|
||||
get_opt_attr<unsigned>(sym, "halo_radius");
|
||||
optional<double> halo_radius =
|
||||
get_opt_attr<double>(sym, "halo_radius");
|
||||
if (halo_radius)
|
||||
{
|
||||
text_symbol.set_halo_radius(*halo_radius);
|
||||
}
|
||||
|
||||
|
||||
// text ratio and wrap width
|
||||
optional<unsigned> text_ratio =
|
||||
get_opt_attr<unsigned>(sym, "text_ratio");
|
||||
|
@ -1233,8 +1233,8 @@ void map_parser::parse_shield_symbolizer( rule_type & rule, ptree const & sym )
|
|||
{
|
||||
shield_symbol.set_halo_fill( * halo_fill );
|
||||
}
|
||||
optional<unsigned> halo_radius =
|
||||
get_opt_attr<unsigned>(sym, "halo_radius");
|
||||
optional<double> halo_radius =
|
||||
get_opt_attr<double>(sym, "halo_radius");
|
||||
if (halo_radius)
|
||||
{
|
||||
shield_symbol.set_halo_radius(*halo_radius);
|
||||
|
@ -1599,12 +1599,12 @@ void map_parser::parse_glyph_symbolizer(rule_type & rule, ptree const &sym)
|
|||
glyph_sym.set_halo_fill(*halo_fill);
|
||||
|
||||
// halo_radius
|
||||
optional<unsigned> halo_radius = get_opt_attr<unsigned>(
|
||||
optional<double> halo_radius = get_opt_attr<double>(
|
||||
sym,
|
||||
"halo_radius");
|
||||
if (halo_radius)
|
||||
glyph_sym.set_halo_radius(*halo_radius);
|
||||
|
||||
|
||||
// allow_overlap
|
||||
optional<boolean> allow_overlap = get_opt_attr<boolean>(
|
||||
sym,
|
||||
|
|
|
@ -397,12 +397,12 @@ color const& text_symbolizer::get_halo_fill() const
|
|||
return halo_fill_;
|
||||
}
|
||||
|
||||
void text_symbolizer::set_halo_radius(unsigned radius)
|
||||
void text_symbolizer::set_halo_radius(double radius)
|
||||
{
|
||||
halo_radius_ = radius;
|
||||
}
|
||||
|
||||
unsigned text_symbolizer::get_halo_radius() const
|
||||
double text_symbolizer::get_halo_radius() const
|
||||
{
|
||||
return halo_radius_;
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue