From bd173527a0a0b3ff18b7189304b17b929f0dbb93 Mon Sep 17 00:00:00 2001 From: Artem Pavlenko Date: Tue, 21 Feb 2006 19:55:24 +0000 Subject: [PATCH] added font_engine impl - work in progress:) --- SConstruct | 5 +- include/agg_renderer.hpp | 7 + include/expression.hpp | 13 +- include/font_engine_freetype.hpp | 395 +++++++++++++++++++++++++++++++ include/graphics.hpp | 25 +- include/text_symbolizer.hpp | 33 ++- include/value.hpp | 22 +- src/SConscript | 3 + src/agg_renderer.cpp | 75 +++++- src/font_engine_freetype.cpp | 27 +++ src/text.cpp | 2 +- 11 files changed, 579 insertions(+), 28 deletions(-) create mode 100644 include/font_engine_freetype.hpp create mode 100644 src/font_engine_freetype.cpp diff --git a/SConstruct b/SConstruct index c4e069754..6ba402721 100644 --- a/SConstruct +++ b/SConstruct @@ -24,8 +24,8 @@ opts = Options() opts.Add('PREFIX', 'The install path "prefix"', '/usr/local') opts.Add(PathOption('BOOST_INCLUDES', 'Search path for boost include files', '/usr/include')) opts.Add(PathOption('BOOST_LIBS', 'Search path for boost library files', '/usr/lib')) -opts.Add(PathOption('FREETYPE_INCLUDES', 'Search path for FreeType include files', '/usr/include')) -opts.Add(PathOption('FREETYPE_LIBS', 'Search path for FreeType library files', '/usr/lib')) +opts.Add(PathOption('FREETYPE_INCLUDES', 'Search path for FreeType include files', '/opt/freetype/include/freetype2')) +opts.Add(PathOption('FREETYPE_LIBS', 'Search path for FreeType library files', '/opt/freetype/lib')) opts.Add(PathOption('PNG_INCLUDES', 'Search path for libpng include files', '/usr/include')) opts.Add(PathOption('PNG_LIBS', 'Search path for libpng include files', '/usr/lib')) opts.Add(PathOption('JPEG_INCLUDES', 'Search path for libjpeg include files', '/usr/include')) @@ -63,6 +63,7 @@ C_LIBSHEADERS = [ ['tiff', 'tiff.h', True], ['z', 'zlib.h', True], ['jpeg', ['stdio.h','jpeglib.h'], True], + #['freetype', 'ft2build.h', True], ['pq', 'libpq-fe.h', False] ] diff --git a/include/agg_renderer.hpp b/include/agg_renderer.hpp index fb3521128..c276055b2 100644 --- a/include/agg_renderer.hpp +++ b/include/agg_renderer.hpp @@ -23,6 +23,8 @@ #include "feature_style_processor.hpp" #include +//#include "agg_font_freetype.h" +#include "font_engine_freetype.hpp" namespace mapnik { @@ -31,6 +33,10 @@ namespace mapnik private boost::noncopyable { agg_renderer(Map const& m, T & pixmap); + void start_map_processing(); + void end_map_processing(); + void start_layer_processing(); + void end_layer_processing(); void process(point_symbolizer const& sym,Feature const& feature); void process(line_symbolizer const& sym,Feature const& feature); void process(line_pattern_symbolizer const& sym,Feature const& feature); @@ -41,6 +47,7 @@ namespace mapnik private: T & pixmap_; CoordTransform t_; + face_manager font_manager_; }; } diff --git a/include/expression.hpp b/include/expression.hpp index e05caf3a0..c92015416 100644 --- a/include/expression.hpp +++ b/include/expression.hpp @@ -68,7 +68,7 @@ namespace mapnik } std::string to_string() const { - return value_.to_string(); + return value_.to_expression_string(); } ~literal() {} private: @@ -88,8 +88,6 @@ namespace mapnik property(property const& other) : expression(), name_(other.name_) - //index_(other.index_), - //valid_(other.valid_) {} value get_value(FeatureT const& feature) const @@ -108,11 +106,7 @@ namespace mapnik { return name_; } - void set_index(size_t index) - { - //index_=index; - //valid_=true; - } + std::string to_string() const { return "["+name_+"]"; @@ -120,8 +114,7 @@ namespace mapnik ~property() {} private: std::string name_; - //size_t index_; - //bool valid_; + }; } diff --git a/include/font_engine_freetype.hpp b/include/font_engine_freetype.hpp new file mode 100644 index 000000000..2ccd5766e --- /dev/null +++ b/include/font_engine_freetype.hpp @@ -0,0 +1,395 @@ + /* This file is part of Mapnik (c++ mapping toolkit) + * Copyright (C) 2005 Artem Pavlenko + * + * Mapnik is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +//$Id$ + +#if !defined FONT_ENGINE_FREETYPE_HPP +#define FONT_ENGINE_FREETYPE_HPP + +#include +#include FT_FREETYPE_H +#include FT_GLYPH_H + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +namespace mapnik +{ + + class font_face : boost::noncopyable + { + public: + font_face(FT_Face face) + : face_(face) {} + + std::string family_name() const + { + return std::string(face_->family_name); + } + + std::string style_name() const + { + return std::string(face_->style_name); + } + + unsigned num_glyphs() const + { + return face_->num_glyphs; + } + + FT_GlyphSlot glyph() const + { + return face_->glyph; + } + + /* + unsigned glyph_index(unsigned charcode) const + { + return FT_Get_Char_Index(face_, charcode ); + } + + void set_transform (FT_Matrix * m,FT_Vector *v) + { + FT_Set_Transform(face_,m,v); + } + + bool render_glyph (unsigned charcode) const + { + unsigned glyph_index = FT_Get_Char_Index(face_, charcode ); + FT_Error error; + error = FT_Load_Glyph(face_,glyph_index,FT_LOAD_DEFAULT); + if (error == 0 ) + { + error = FT_Render_Glyph(face_->glyph, FT_RENDER_MODE_NORMAL ); + if (error == 0) + { + return true; + } + } + return false; + } + */ + + FT_Face get_face() const + { + return face_; + } + + bool set_pixel_sizes(unsigned size) + { + if (! FT_Set_Pixel_Sizes( face_, 0, size )) + return true; + return false; + } + + + ~font_face() + { + std::cout << "clean up face:" << family_name()<<":" << style_name() << std::endl; + FT_Done_Face(face_); + } + + private: + FT_Face face_; + }; + + typedef boost::shared_ptr face_ptr; + + class freetype_engine : public mapnik::singleton, + private boost::noncopyable + { + friend class mapnik::CreateStatic; + public: + + static bool register_font(std::string const& file_name) + { + mutex::scoped_lock lock(mapnik::singleton::mutex_); + FT_Face face; + FT_Error error = FT_New_Face (library_,file_name.c_str(),0,&face); + if ( !error ) + { + std::string name = std::string(face->family_name) + " " + std::string(face->style_name); + name2file_.insert(std::make_pair(name,file_name)); + FT_Done_Face(face ); + return true; + } + return false; + } + + static std::vector face_names () + { + std::vector names; + std::map::const_iterator itr; + for (itr = name2file_.begin();itr!=name2file_.end();++itr) + { + names.push_back(itr->first); + } + return names; + } + + static face_ptr create_face(std::string const& family_name) + { + mutex::scoped_lock lock(mapnik::singleton::mutex_); + + std::map::iterator itr; + itr = name2file_.find(family_name); + if (itr != name2file_.end()) + { + FT_Face face; + FT_Error error = FT_New_Face (library_,itr->second.c_str(),0,&face); + + if (!error) + { + return face_ptr (new font_face(face)); + } + } + return face_ptr(); + } + + private: + freetype_engine() + { + FT_Error error = FT_Init_FreeType( &library_ ); + if (error) + { + throw std::runtime_error("can not load FreeType2 library"); + } + } + virtual ~freetype_engine() + { + FT_Done_FreeType(library_); + } + + static FT_Library library_; + static std::map name2file_; + }; + + + template + class face_manager : private boost::noncopyable + { + typedef T font_engine_type; + typedef std::map faces; + + public: + face_ptr get_face(std::string const& name) + { + typename faces::iterator itr; + itr = faces_.find(name); + if (itr != faces_.end()) + { + return itr->second; + } + else + { + face_ptr face = font_engine_type::instance()->create_face(name); + if (face) + { + faces_.insert(make_pair(name,face)); + } + return face; + } + } + private: + faces faces_; + }; + + template + struct text_renderer : private boost::noncopyable + { + typedef T pixmap_type; + + text_renderer (pixmap_type & pixmap, face_ptr face) + : pixmap_(pixmap), + face_(face), + fill_(0,0,0), + halo_fill_(255,255,255), + halo_radius_(0), + angle_(0.0) {} + + void set_pixel_size(unsigned size) + { + face_->set_pixel_sizes(size); + } + + void set_angle(float angle) + { + angle_=angle; + } + + void set_fill(mapnik::Color const& fill) + { + fill_=fill; + } + + void set_halo_fill(mapnik::Color const& halo) + { + halo_fill_=halo; + } + + void set_halo_radius( int radius=1) + { + halo_radius_=radius; + } + + void render(std::string const& text, double x0, double y0) + { + FT_Matrix matrix; + FT_Vector origin; + FT_Vector pen; + FT_Error error; + FT_Glyph glyph; + + FT_Face face = face_->get_face(); + FT_GlyphSlot slot = face->glyph; + FT_UInt glyph_index; + FT_Bool use_kerning; + FT_UInt previous; + + unsigned height = pixmap_.height(); + + origin.x = 0; + origin.y = 0; + pen.x = unsigned(x0 * 64); + pen.y = unsigned((height - y0) * 64); + + use_kerning = FT_HAS_KERNING(face); + + //unsigned count=1; + + for (std::string::const_iterator i=text.begin();i!=text.end();++i) + { + + matrix.xx = (FT_Fixed)( cos( angle_ ) * 0x10000L ); + matrix.xy = (FT_Fixed)(-sin( angle_ ) * 0x10000L ); + matrix.yx = (FT_Fixed)( sin( angle_ ) * 0x10000L ); + matrix.yy = (FT_Fixed)( cos( angle_ ) * 0x10000L ); + + FT_Set_Transform (face,&matrix,&pen); + + glyph_index = FT_Get_Char_Index( face, *i ); + if ( use_kerning && previous && glyph_index) + { + FT_Vector delta; + FT_Get_Kerning(face,previous,glyph_index, + FT_KERNING_DEFAULT,&delta); + pen.x += delta.x; + pen.y += delta.y; + std::cout<< "use kerning "<< std::endl; + } + + error = FT_Load_Glyph (face,glyph_index,FT_LOAD_DEFAULT); + if ( error ) + continue; + + error = FT_Get_Glyph( face->glyph, &glyph ); + if ( error ) + continue; + + //FT_Glyph_Transform(glyph,&matrix,&pen); + + error = FT_Glyph_To_Bitmap( &glyph,FT_RENDER_MODE_NORMAL,0,1); + if ( error ) + continue; + + FT_BitmapGlyph bit = (FT_BitmapGlyph)glyph; + if (halo_radius_) + { + render_halo(&bit->bitmap, halo_fill_.rgba(), + bit->left, + height - bit->top,halo_radius_); + } + + render_bitmap(&bit->bitmap, fill_.rgba(), + bit->left, + height - bit->top); + + FT_Done_Glyph(glyph); + pen.x += slot->advance.x; + pen.y += slot->advance.y; + + previous = glyph_index; + + //angle_ = sin ( 0.1 * count++); + + } + } + private: + + void render_halo(FT_Bitmap *bitmap,unsigned rgba,int x,int y,int radius) + { + int x_max=x+bitmap->width; + int y_max=y+bitmap->rows; + int i,p,j,q; + + for (i=x,p=0;ibuffer[q*bitmap->width+p]; + if (gray) + { + for (int n=-halo_radius_; n <=halo_radius_; ++n) + for (int m=-halo_radius_;m <= halo_radius_; ++m) + pixmap_.blendPixel(i+m,j+n,rgba,gray); + } + } + } + } + + void render_bitmap(FT_Bitmap *bitmap,unsigned rgba,int x,int y) + { + int x_max=x+bitmap->width; + int y_max=y+bitmap->rows; + int i,p,j,q; + + for (i=x,p=0;ibuffer[q*bitmap->width+p]; + if (gray) + { + pixmap_.blendPixel(i,j,rgba,gray); + } + } + } + } + + pixmap_type & pixmap_; + face_ptr face_; + mapnik::Color fill_; + mapnik::Color halo_fill_; + int halo_radius_; + float angle_; + }; +} + + +#endif // FONT_ENGINE_FREETYPE_HPP diff --git a/include/graphics.hpp b/include/graphics.hpp index ccfb9f2e9..157f8790b 100644 --- a/include/graphics.hpp +++ b/include/graphics.hpp @@ -95,13 +95,30 @@ namespace mapnik return 0xff << 24 | r << 16 | g << 8 | b; } - inline void blendPixel(int x,int y,unsigned int rgba,int t) + inline void blendPixel(int x,int y,unsigned int rgba1,int t) { if (checkBounds(x,y)) { - int bg=data_(x,y); - int nc=blendColor(rgba,bg,t); - data_(x,y)=nc; + unsigned rgba0 = data_(x,y); + unsigned a1 = t;//(rgba1 >> 24) & 0xff; + if (a1 == 0) return; + unsigned r1 = rgba1 & 0xff; + unsigned g1 = (rgba1 >> 8 ) & 0xff; + unsigned b1 = (rgba1 >> 16) & 0xff; + + unsigned a0 = (rgba0 >> 24) & 0xff; + unsigned r0 = (rgba0 & 0xff) * a0; + unsigned g0 = ((rgba0 >> 8 ) & 0xff) * a0; + unsigned b0 = ((rgba0 >> 16) & 0xff) * a0; + + + a0 = ((a1 + a0) << 8) - a0*a1; + + r0 = ((((r1 << 8) - r0) * a1 + (r0 << 8)) / a0); + g0 = ((((g1 << 8) - g0) * a1 + (g0 << 8)) / a0); + b0 = ((((b1 << 8) - b0) * a1 + (b0 << 8)) / a0); + a0 = a0 >> 8; + data_(x,y)= (a0 << 24)| (b0 << 16) | (g0 << 8) | (r0) ; } } diff --git a/include/text_symbolizer.hpp b/include/text_symbolizer.hpp index 948dffff4..95271fca2 100644 --- a/include/text_symbolizer.hpp +++ b/include/text_symbolizer.hpp @@ -27,15 +27,22 @@ namespace mapnik { + enum label_placement_e { + point_placement=1, + line_placement=2 + }; + struct text_symbolizer - { + { text_symbolizer(std::string const& name,Color const& fill) : name_(name), - fill_(fill) {} - + fill_(fill), + label_p_(point_placement){} + text_symbolizer(text_symbolizer const& rhs) : name_(rhs.name_), - fill_(rhs.fill_) {} + fill_(rhs.fill_), + label_p_(rhs.label_p_) {} ~text_symbolizer() { @@ -45,9 +52,25 @@ namespace mapnik { return name_; } + Color const& get_fill() const + { + return fill_; + } + + void set_label_placement(label_placement_e label_p) + { + label_p_ = label_p; + } + + label_placement_e get_label_placement() const + { + return label_p_; + } + private: std::string name_; - Color fill_; + Color fill_; + label_placement_e label_p_; }; } diff --git a/include/value.hpp b/include/value.hpp index e6ec2677c..19c83e003 100644 --- a/include/value.hpp +++ b/include/value.hpp @@ -313,6 +313,21 @@ namespace mapnik { }; struct to_string : public boost::static_visitor + { + template + std::string operator() (T val) const + { + std::stringstream ss; + ss << val; + return ss.str(); + } + std::string const& operator() (std::string const& val) const + { + return val; + } + }; + + struct to_expression_string : public boost::static_visitor { template std::string operator() (T val) const @@ -390,7 +405,12 @@ namespace mapnik { *this = boost::apply_visitor(impl::div(),*this,other); return *this; } - + + std::string to_expression_string() const + { + return boost::apply_visitor(impl::to_expression_string(),*this); + } + std::string to_string() const { return boost::apply_visitor(impl::to_string(),*this); diff --git a/src/SConscript b/src/SConscript index 150da28f6..c671030da 100644 --- a/src/SConscript +++ b/src/SConscript @@ -24,6 +24,8 @@ Import('env') prefix = env['PREFIX'] libraries = ['agg'] + env['LIBS'] +libraries += ['freetype'] + linkflags = '-Wl,-rpath-link,. -Wl,-soname,libmapnik.so' source = Split( @@ -47,6 +49,7 @@ source = Split( point_symbolizer.cpp polygon_pattern_symbolizer.cpp line_pattern_symbolizer.cpp + font_engine_freetype.cpp """ ) #render.cpp diff --git a/src/agg_renderer.cpp b/src/agg_renderer.cpp index cc7d84fd7..2334025c4 100644 --- a/src/agg_renderer.cpp +++ b/src/agg_renderer.cpp @@ -51,7 +51,6 @@ #include "agg_renderer_outline_image.h" #include - #include namespace mapnik @@ -89,6 +88,31 @@ namespace mapnik pixmap_.setBackground(bg); std::cout << "scale="< + void agg_renderer::start_map_processing() + { + std::cout << "start map processing" << std::endl; + } + + template + void agg_renderer::end_map_processing() + { + std::cout << "end map processing" << std::endl; + } + + template + void agg_renderer::start_layer_processing() + { + std::cout << "start layer processing" << std::endl; + } + + template + void agg_renderer::end_layer_processing() + { + std::cout << "end layer processing" << std::endl; + } + template void agg_renderer::process(polygon_symbolizer const& sym,Feature const& feature) { @@ -356,10 +380,51 @@ namespace mapnik template void agg_renderer::process(text_symbolizer const& sym ,Feature const& feature) { - //TODO - implement text - //std::cout << feature << std::endl; - std::cout << sym.get_name() <<":" << feature[sym.get_name()] << std::endl; + typedef coord_transform path_type; + geometry_ptr const& geom=feature.get_geometry(); + if (geom) + { + double angle = 0.0; + if (sym.get_label_placement() == line_placement && + geom->num_points() > 1) + { + + path_type path(t_,*geom); + double x0,y0,x1,y1; + path.vertex(&x0,&y0); + path.vertex(&x1,&y1); + double dx = x1 - x0; + double dy = y1 - y0; + angle = atan( dx/ dy ) - 0.5 * 3.1459; + + //TODO!!!!!!!!!!!!!!!!!!!! + } + + std::string text = feature[sym.get_name()].to_string(); + if (text.length() > 0) + { + Color const& fill = sym.get_fill(); + + double x; + double y; + geom->label_position(&x,&y); + t_.forward_x(&x); + t_.forward_y(&y); + + face_ptr face = font_manager_.get_face("Bitstream Vera Sans Roman");//TODO + + if (face) + { + text_renderer ren(pixmap_,face); + ren.set_pixel_size(12); + ren.set_fill(fill); + ren.set_halo_radius(1); + ren.set_angle(angle); + ren.render(text,x,y); + } + } + } } - + template class agg_renderer; } diff --git a/src/font_engine_freetype.cpp b/src/font_engine_freetype.cpp new file mode 100644 index 000000000..cb1aa2e9f --- /dev/null +++ b/src/font_engine_freetype.cpp @@ -0,0 +1,27 @@ +/* This file is part of Mapnik (c++ mapping toolkit) + * Copyright (C) 2006 Artem Pavlenko + * + * Mapnik is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +//$Id$ + +#include "font_engine_freetype.hpp" + +namespace mapnik +{ + FT_Library freetype_engine::library_; + std::map freetype_engine::name2file_; +} diff --git a/src/text.cpp b/src/text.cpp index c7117e6e6..bfbd5c6c4 100644 --- a/src/text.cpp +++ b/src/text.cpp @@ -69,7 +69,7 @@ namespace mapnik } template - void TextRasterizer::render_bitmap(FT_Bitmap *bitmap,int x,int y) + void TextRasterizer::render_bitmap(FT_Bitmap *bitmap,int x,int y) { int x_max=x+bitmap->width; int y_max=y+bitmap->rows;