+ 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:
Artem Pavlenko 2010-06-16 15:15:13 +00:00
parent f0d579f742
commit e645338237
8 changed files with 116 additions and 47 deletions

View file

@ -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_;
};

View file

@ -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_;

View file

@ -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;

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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,

View file

@ -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_;
}