Warning, /include/Geant4/toolx/sg/text_freetype is written in an unsupported language. File is not indexed.
0001 // Copyright (C) 2010, Guy Barrand. All rights reserved.
0002 // See the file tools.license for terms.
0003
0004 #ifndef toolx_sg_text_freetype
0005 #define toolx_sg_text_freetype
0006
0007
0008 #include <tools/sg/base_freetype>
0009 #include <tools/sg/render_action>
0010 #include <tools/sg/pick_action>
0011 #include <tools/sg/bbox_action>
0012 #include <tools/sg/enums>
0013 #include <tools/lina/vec3d>
0014 #include <tools/file>
0015 #include <tools/nostream>
0016 #include <tools/lina/box_3f>
0017
0018 // font_pixmap :
0019 #include <tools/sg/group>
0020 #include <tools/sg/blend>
0021 #include <tools/sg/tex_quadrilateral>
0022
0023 #include <tools/glutess/glutess>
0024
0025 #include <map> //for errors
0026
0027 #ifndef SWIG
0028 #include <ft2build.h>
0029 #include FT_FREETYPE_H
0030 #include FT_GLYPH_H
0031 #include FT_OUTLINE_H
0032 #endif
0033
0034 namespace toolx {
0035 namespace sg {
0036
0037 class text_freetype : public tools::sg::base_freetype {
0038 TOOLS_NODE(text_freetype,toolx::sg::text_freetype,tools::sg::base_freetype)
0039 protected:
0040 text_freetype& self() const {return const_cast<text_freetype&>(*this);}
0041 protected:
0042 TOOLS_CLASS_STRING(TOOLS_FONT_PATH)
0043 protected:
0044 enum wndg_type {
0045 wndg_ccw,
0046 wndg_cw,
0047 wndg_not_done
0048 };
0049 protected: //gstos
0050 virtual unsigned int create_gsto(std::ostream& a_out,tools::sg::render_manager& a_mgr) {
0051 std::vector<float> gsto_data;
0052
0053 {tools_vforcit(line_t,m_lines,it) {
0054 const line_t& item = *it;
0055 size_t pos = item.first;
0056 size_t num = item.second;
0057
0058 if(num<2) {
0059 a_out << "toolx::sg::text_freetype::create_gsto :"
0060 << " strange line with " << num << " points."
0061 << std::endl;
0062 continue; //do we have the case num = 1 ?
0063 }
0064
0065 const float* data = m_xys.data()+pos;
0066
0067 // data is a line_strip(), convert it to lines.
0068
0069 size_t nsegs = num-1;
0070
0071 size_t ngsto = nsegs*2*3; //3 = have a z (Windows GL).
0072 size_t sz = gsto_data.size();
0073 gsto_data.resize(sz+ngsto);
0074 float* pxyz = gsto_data.data()+sz;
0075
0076 tools::gl::line_strip_to_lines_2to3(num,data,pxyz);
0077 }}
0078
0079 m_gsto_lines_sz = gsto_data.size();
0080
0081 {tools_vforcit(gl_triangle_t,m_triangles,it) {
0082 const std::pair<GLUenum,triangle_t>& item = *it;
0083 size_t pos = item.second.first;
0084 size_t num = item.second.second;
0085
0086 if(num<3) {
0087 a_out << "toolx::sg::text_freetype::create_gsto :"
0088 << " strange triangle primitive with " << num << " points."
0089 << " Primitive kind is " << (*it).first << "."
0090 << std::endl;
0091 continue;
0092 }
0093
0094 const float* data = m_xys.data()+pos;
0095
0096 if((*it).first==tools::gl::triangles()) {
0097
0098 size_t ntri = num/3;
0099
0100 size_t ngsto = ntri*3*3;
0101 size_t sz = gsto_data.size();
0102 gsto_data.resize(sz+ngsto);
0103 float* pxyz = gsto_data.data()+sz;
0104
0105 tools::gl::cvt_2to3(num,data,pxyz);
0106
0107 } else if((*it).first==tools::gl::triangle_fan()) {
0108
0109 size_t ntri = num-2;
0110
0111 size_t ngsto = ntri*3*3;
0112 size_t sz = gsto_data.size();
0113 gsto_data.resize(sz+ngsto);
0114 float* pxyz = gsto_data.data()+sz;
0115
0116 tools::gl::triangle_fan_to_triangles_2to3(num,data,pxyz);
0117
0118 } else if((*it).first==tools::gl::triangle_strip()) {
0119
0120 size_t ntri = num-2;
0121
0122 size_t ngsto = ntri*3*3;
0123 size_t sz = gsto_data.size();
0124 gsto_data.resize(sz+ngsto);
0125 float* pxyz = gsto_data.data()+sz;
0126
0127 tools::gl::triangle_strip_to_triangles_2to3(num,data,pxyz);
0128
0129 } else {
0130 a_out << "toolx::sg::text_freetype::create_gsto :"
0131 << " unknown triangle primitive kind " << (*it).first << "."
0132 << std::endl;
0133 }
0134
0135 }}
0136
0137 m_gsto_sz = gsto_data.size();
0138
0139 if(gsto_data.empty()) {
0140 return 0;
0141 }
0142
0143 return a_mgr.create_gsto_from_data(gsto_data);
0144 }
0145
0146 public:
0147 virtual void render(tools::sg::render_action& a_action) {
0148 {bool _color_touched = color_touched(a_action.state());
0149 bool _char_height_touched = char_height_touched(a_action.state());
0150 if(touched()||_color_touched||_char_height_touched) {
0151 update_sg(a_action.out(),font.touched());
0152 reset_touched();
0153 }}
0154
0155 const tools::sg::state& state = a_action.state();
0156
0157 if(m_wndg==wndg_ccw) a_action.set_winding(tools::sg::winding_ccw);
0158 else if(m_wndg==wndg_cw) a_action.set_winding(tools::sg::winding_cw);
0159
0160 if(state.m_use_gsto) {
0161 unsigned int _id = get_gsto_id(a_action.out(),a_action.render_manager());
0162 if(_id) {
0163 a_action.begin_gsto(_id);
0164 size_t sz_tris = m_gsto_sz-m_gsto_lines_sz;
0165 if(m_gsto_lines_sz) {
0166 a_action.set_line_smooth(true);
0167 a_action.draw_gsto_v(tools::gl::lines(),m_gsto_lines_sz/3,0);
0168 a_action.set_polygon_offset(true);
0169 }
0170 if(m_gsto_lines_sz && sz_tris) {
0171 a_action.set_line_smooth(state.m_GL_LINE_SMOOTH);
0172 a_action.set_polygon_offset(state.m_GL_POLYGON_OFFSET_FILL);
0173 }
0174 tools::sg::bufpos pos = m_gsto_lines_sz*sizeof(float);
0175 a_action.draw_gsto_v(tools::gl::triangles(),sz_tris/3,pos);
0176 a_action.end_gsto();
0177
0178 a_action.set_line_smooth(state.m_GL_LINE_SMOOTH);
0179 a_action.set_polygon_offset(state.m_GL_POLYGON_OFFSET_FILL);
0180 a_action.set_winding(state.m_winding);
0181 return;
0182 } else {
0183 // use immediate rendering.
0184 }
0185 } else {
0186 clean_gstos(&a_action.render_manager());
0187 }
0188
0189 // immediate rendering :
0190
0191 ///////////////////////////////////////////////////////////
0192 /// lines /////////////////////////////////////////////////
0193 ///////////////////////////////////////////////////////////
0194
0195 a_action.set_line_smooth(true);
0196
0197 {tools_vforcit(line_t,m_lines,it) {
0198 const line_t& item = *it;
0199 size_t pos = item.first;
0200 size_t num = item.second;
0201 if(!num) continue;
0202
0203 const float* data = m_xys.data()+pos;
0204
0205 a_action.draw_vertex_array_xy(tools::gl::line_strip(),num*2,data);
0206 }}
0207
0208 if(m_lines.size()) a_action.set_polygon_offset(true);
0209
0210 ///////////////////////////////////////////////////////////
0211 /// triangles /////////////////////////////////////////////
0212 ///////////////////////////////////////////////////////////
0213 {tools_vforcit(gl_triangle_t,m_triangles,it) {
0214 const std::pair<GLUenum,triangle_t>& item = *it;
0215 size_t pos = item.second.first;
0216 size_t num = item.second.second;
0217 if(!num) continue;
0218
0219 const float* data = m_xys.data()+pos;
0220
0221 a_action.draw_vertex_array_xy((*it).first,num*2,data);
0222 }}
0223
0224 a_action.set_line_smooth(state.m_GL_LINE_SMOOTH);
0225 a_action.set_polygon_offset(state.m_GL_POLYGON_OFFSET_FILL);
0226 a_action.set_winding(state.m_winding);
0227
0228 ///////////////////////////////////////////////////////////
0229 /// bitmap ////////////////////////////////////////////////
0230 ///////////////////////////////////////////////////////////
0231 m_bitmaps.render(a_action);
0232 }
0233
0234 virtual void pick(tools::sg::pick_action& a_action) {
0235 {bool _char_height_touched = char_height_touched(a_action.state());
0236 if(touched()||_char_height_touched) {
0237 update_sg(a_action.out(),font.touched());
0238 reset_touched();
0239 }}
0240
0241 ///////////////////////////////////////////////////////////
0242 /// lines /////////////////////////////////////////////////
0243 ///////////////////////////////////////////////////////////
0244
0245 {tools_vforcit(line_t,m_lines,it) {
0246 const line_t& item = *it;
0247 size_t pos = item.first;
0248 size_t num = item.second;
0249 const float* data = m_xys.data()+pos;
0250 if(a_action.add__line_strip_xy(*this,2*num,data,true)) return;
0251 }}
0252
0253
0254 ///////////////////////////////////////////////////////////
0255 /// triangles /////////////////////////////////////////////
0256 ///////////////////////////////////////////////////////////
0257 {tools_vforcit(gl_triangle_t,m_triangles,it) {
0258 const std::pair<GLUenum,triangle_t>& item = *it;
0259 size_t pos = item.second.first;
0260 size_t num = item.second.second;
0261 const float* data = m_xys.data()+pos;
0262 if(a_action.add__primitive_xy(*this,item.first,2*num,data,true)) return;
0263 }}
0264
0265 ///////////////////////////////////////////////////////////
0266 /// bitmap ////////////////////////////////////////////////
0267 ///////////////////////////////////////////////////////////
0268 m_bitmaps.pick(a_action);
0269 }
0270
0271 virtual void bbox(tools::sg::bbox_action& a_action) {
0272 {bool _char_height_touched = char_height_touched(a_action.state());
0273 if(touched()||_char_height_touched) {
0274 update_sg(a_action.out(),font.touched());
0275 reset_touched();
0276 }}
0277
0278 {tools_vforcit(line_t,m_lines,it) {
0279 const line_t& item = *it;
0280 size_t num = item.second;
0281 const float* data = m_xys.data()+item.first;
0282
0283 float px,py,pz;
0284 float* pos = (float*)data;
0285 for(size_t index=0;index<num;index++) {
0286 px = *pos;pos++;
0287 py = *pos;pos++;
0288 pz = 0;
0289 a_action.add_one_point(px,py,pz);
0290 }
0291
0292 }}
0293
0294 ///////////////////////////////////////////////////////////
0295 /// triangles /////////////////////////////////////////////
0296 ///////////////////////////////////////////////////////////
0297 {tools_vforcit(gl_triangle_t,m_triangles,it) {
0298 const std::pair<GLUenum,triangle_t>& item = *it;
0299 size_t num = item.second.second;
0300 const float* data = m_xys.data()+item.second.first;
0301
0302 float px,py,pz;
0303 float* pos = (float*)data;
0304 for(size_t index=0;index<num;index++) {
0305 px = *pos;pos++;
0306 py = *pos;pos++;
0307 pz = 0;
0308 a_action.add_one_point(px,py,pz);
0309 }
0310 }}
0311
0312 ///////////////////////////////////////////////////////////
0313 /// bitmap ////////////////////////////////////////////////
0314 ///////////////////////////////////////////////////////////
0315 m_bitmaps.bbox(a_action);
0316 }
0317
0318 public:
0319 text_freetype()
0320 :parent()
0321
0322 ,m_library(0)
0323 ,m_face(0)
0324 ,m_encoding_offset(0)
0325 ,m_verbose(false)
0326 ,m_scale(1)
0327 ,m_pos(0)
0328 ,m_char_height(0)
0329 {
0330 if(!initialize()){} //throw
0331 }
0332 virtual ~text_freetype(){
0333 if(m_face) ::FT_Done_Face(m_face);
0334 if(m_library) ::FT_Done_FreeType(m_library);
0335 clear_trids();
0336 }
0337 public:
0338 text_freetype(const text_freetype& a_from)
0339 :parent(a_from)
0340
0341 ,m_library(0)
0342 ,m_face(0)
0343 ,m_encoding_offset(0)
0344 ,m_verbose(a_from.m_verbose)
0345 ,m_scale(1)
0346 ,m_pos(0)
0347 ,m_char_height(0)
0348 {
0349 if(!initialize()){} //throw
0350 }
0351
0352 text_freetype& operator=(const text_freetype& a_from){
0353 parent::operator=(a_from);
0354 if(&a_from==this) return *this;
0355
0356 if(m_face) {::FT_Done_Face(m_face);m_face = 0;}
0357 if(m_library) {::FT_Done_FreeType(m_library);m_library = 0;}
0358 clear_trids();
0359
0360 m_encoding_offset = 0;
0361 m_verbose = a_from.m_verbose;
0362 m_scale = 1;
0363 m_char_height = 0;
0364
0365 if(!initialize()){} //throw
0366
0367 return *this;
0368 }
0369 public: //tools::sg::base_text :
0370 virtual float ascent(float a_height) const {
0371 tools::nostream out;
0372 if(!m_face) self().load_face(out);
0373 if(!m_face) return 0;
0374 float value;
0375 if(!ascent(out,self().m_face,a_height,value)) return 0;
0376 return value;
0377 }
0378
0379 virtual float descent(float a_height) const {
0380 tools::nostream out;
0381 if(!m_face) self().load_face(out);
0382 if(!m_face) return 0;
0383 float value;
0384 if(!descent(out,self().m_face,a_height,value)) return 0;
0385 return value;
0386 }
0387
0388 virtual float y_advance(float a_height) const {
0389 tools::nostream out;
0390 if(!m_face) self().load_face(out);
0391 if(!m_face) return 0;
0392 float value;
0393 if(!y_advance(out,self().m_face,a_height,value)) return 0;
0394 return value;
0395 }
0396
0397 virtual void get_bounds(float a_height,
0398 float& a_mn_x,float& a_mn_y,float& a_mn_z,
0399 float& a_mx_x,float& a_mx_y,float& a_mx_z) const {
0400 tools::nostream out;
0401 if(!m_face) self().load_face(out);
0402 if(!m_face) return;
0403 if(strings.values().size()) {
0404 if(!get_bounds(out,self().m_face,a_height,strings.values(),a_mn_x,a_mn_y,a_mn_z,a_mx_x,a_mx_y,a_mx_z)) return;
0405 } else if(unitext.values().size()) {
0406 if(!get_bounds(out,self().m_face,a_height,unitext.values(),a_mn_x,a_mn_y,a_mn_z,a_mx_x,a_mx_y,a_mx_z)) return;
0407 }
0408 }
0409
0410 virtual bool truncate(const std::string& a_string,float a_height,float a_cut_width,std::string& a_out) const {
0411 a_out.clear();
0412 tools::nostream out;
0413 if(!m_face) self().load_face(out);
0414 if(!m_face) return false;
0415 if(!truncate(out,self().m_face,a_height,a_string,a_cut_width,a_out)) return false;
0416 return true;
0417 }
0418
0419 void dump_unitext(std::ostream& a_out) {
0420 a_out << "unitext size : " << unitext.values().size() << std::endl;
0421 tools_vforcit(uniline,unitext.values(),vit) {
0422 const uniline& line = *vit;
0423 a_out << "beg line :" << std::endl;
0424 tools_vforcit(unichar,line,it) {
0425 a_out << ((unsigned int)*it) << std::endl;
0426 }
0427 a_out << "end line." << std::endl;
0428 }
0429 }
0430
0431 protected:
0432 bool initialize() { //called from constructors.
0433 FT_Error error = ::FT_Init_FreeType(&m_library);
0434 if(error) {
0435 m_library = 0;
0436 return false;
0437 }
0438 // cast because of const/not const according freetype version.
0439 // (Recent version have "const FT_Vector*" in args).
0440 m_funcs.move_to = (FT_Outline_MoveToFunc)outline_move_to;
0441 m_funcs.line_to = (FT_Outline_LineToFunc)outline_line_to;
0442 m_funcs.conic_to = (FT_Outline_ConicToFunc)outline_conic_to;
0443 m_funcs.cubic_to = (FT_Outline_CubicToFunc)outline_cubic_to;
0444 m_funcs.shift = 0;
0445 m_funcs.delta = 0;
0446
0447 // Comment from OGLFT :
0448 // Default number of steps to break TrueType and Type1 arcs into.
0449 // (Note: this looks good to me, anyway)
0450 m_steps = 4;
0451 m_delta = 1.0f /(float)m_steps;
0452 m_delta2 = m_delta * m_delta;
0453 m_delta3 = m_delta2 * m_delta;
0454
0455 return true;
0456 }
0457
0458 ////////////////////////////////////////////////////////
0459 /// outline lines //////////////////////////////////////
0460 ////////////////////////////////////////////////////////
0461 enum update_what {
0462 faces = 0,
0463 lines = 1,
0464 faces_and_lines
0465 };
0466
0467 bool color_touched(const tools::sg::state& a_state) {
0468 if(modeling!=tools::sg::font_pixmap) return false;
0469 if(a_state.m_color==m_front_color) return false;
0470 m_front_color = a_state.m_color;
0471 return true;
0472 }
0473
0474 bool char_height_touched(const tools::sg::state& a_state) {
0475 if(modeling!=tools::sg::font_pixmap) return false;
0476
0477 float ymn,ymx;
0478 {float x,y,z,w;
0479 x = 0;y = -height.value()*0.5f;z = 0;
0480 if(!a_state.project_point(x,y,z,w)) return false;
0481 ymn = y;
0482 x = 0;y = height.value()*0.5f;z = 0;
0483 if(!a_state.project_point(x,y,z,w)) return false;
0484 ymx = y;}
0485 float screen_height = ymx-ymn;
0486 if(a_state.m_wh) screen_height *= a_state.m_wh; else screen_height = 100;
0487
0488 if(screen_height==m_char_height) return false;
0489 m_char_height = screen_height;
0490 return true;
0491 }
0492
0493 void update_sg(std::ostream& a_out,bool a_load_font) {
0494 if(a_load_font) load_face(a_out);
0495
0496 clean_gstos(); //must reset for all render_manager.
0497
0498 if(!m_face) return;
0499
0500 m_encoding_offset = 0;
0501 if(!encoding_offset(m_face,m_encoding_offset)) {
0502 a_out << "toolx::sg::text_freetype::update_sg :"
0503 << " encoding_offset failed."
0504 << std::endl;
0505 ::FT_Done_Face(m_face);
0506 m_face = 0;
0507 return;
0508 }
0509
0510 m_xys.clear();
0511 m_pos = 0;
0512 m_lines.clear();
0513 m_triangles.clear();
0514 m_bitmaps.clear();
0515 m_tqs.clear();
0516
0517 m_wndg = wndg_not_done;
0518
0519 if(modeling==tools::sg::font_pixmap) {
0520 if(m_char_height<=0) return;
0521 tools::sg::blend* blend = new tools::sg::blend;
0522 blend->on = true; //to handle background transparency.
0523 m_bitmaps.add(blend);
0524 if(!bitmap_2_gl(a_out)) {m_bitmaps.clear();m_tqs.clear();return;}
0525 } else {
0526 update_what _what = lines;
0527 if(modeling==tools::sg::font_filled) _what = faces;
0528 if((_what==faces)||(_what==faces_and_lines)) outline_triangles_2_gl(a_out);
0529 if((_what==lines)||(_what==faces_and_lines)) outline_lines_2_gl(a_out);
0530 }
0531
0532 if(vjust==tools::sg::bottom) {
0533 } else if(vjust==tools::sg::middle) {
0534 float mn_x,mn_y,mn_z;
0535 float mx_x,mx_y,mx_z;
0536 get_bounds(height,mn_x,mn_y,mn_z,mx_x,mx_y,mx_z);
0537 float szy = mx_y - mn_y;
0538
0539 {tools_vforit(line_t,m_lines,it) {
0540 line_t& item = *it;
0541 size_t pos = item.first;
0542 size_t npt = item.second;
0543 float* data = m_xys.data()+pos+1;
0544 for(size_t i=0;i<npt;i++,data+=2) *data -= 0.5F * szy;
0545 }}
0546
0547 {tools_vforit(gl_triangle_t,m_triangles,it) {
0548 std::pair<GLUenum,triangle_t>& item = *it;
0549 size_t pos = item.second.first;
0550 size_t npt = item.second.second;
0551 float* data = m_xys.data()+pos+1;
0552 for(size_t i=0;i<npt;i++,data+=2) *data -= 0.5F * szy;
0553 }}
0554
0555 {tools_vforcit(tools::sg::tex_quadrilateral*,m_tqs,itqs) {
0556 std::vector<tools::vec3f>& vcs = (*itqs)->corners.values();
0557 for(size_t i=0;i<4;i++) vcs[i][1] -= 0.5f * szy;
0558 }}
0559
0560 } else if(vjust==tools::sg::top) {
0561 float mn_x,mn_y,mn_z;
0562 float mx_x,mx_y,mx_z;
0563 get_bounds(height,mn_x,mn_y,mn_z,mx_x,mx_y,mx_z);
0564 float szy = mx_y - mn_y;
0565
0566 {tools_vforit(line_t,m_lines,it) {
0567 line_t& item = *it;
0568 size_t pos = item.first;
0569 size_t npt = item.second;
0570 float* data = m_xys.data()+pos+1;
0571 for(size_t i=0;i<npt;i++,data+=2) *data -= szy;
0572 }}
0573
0574 {tools_vforit(gl_triangle_t,m_triangles,it) {
0575 std::pair<GLUenum,triangle_t>& item = *it;
0576 size_t pos = item.second.first;
0577 size_t npt = item.second.second;
0578 float* data = m_xys.data()+pos+1;
0579 for(size_t i=0;i<npt;i++,data+=2) *data -= szy;
0580 }}
0581
0582 {tools_vforcit(tools::sg::tex_quadrilateral*,m_tqs,itqs) {
0583 std::vector<tools::vec3f>& vcs = (*itqs)->corners.values();
0584 for(size_t i=0;i<4;i++) vcs[i][1] -= szy;
0585 }}
0586
0587 }
0588
0589 m_tqs.clear();
0590 }
0591
0592 void outline_lines_2_gl(std::ostream& a_out) {
0593 if(!set_char_size(a_out,m_face,height.value(),m_scale)) return;
0594 FT_Pos face_height = m_face->size->metrics.height; //FT_Pos (long)
0595
0596 m_tobj = 0; //IMPORTANT.
0597
0598 if(strings.values().size()) {
0599 float yline = 0;
0600 tools_vforcit(std::string,strings.values(),vit) {
0601 const std::string& line = *vit;
0602 //a_out << line << std::endl;
0603 m_trans_x = 0;
0604 m_trans_y = yline;
0605 size_t ibeg = m_lines.size(); //for hjust.
0606 for(auto it = line.cbegin(); it != line.cend(); ++it) {
0607 if(!char_outline_2_gl(a_out,*it + m_encoding_offset)) return;
0608 }
0609 yline += -float(face_height)*m_scale; //height >0
0610
0611 {float sx = m_trans_x;
0612 if(hjust==tools::sg::center) {
0613 size_t num = m_lines.size();
0614 for(size_t index=ibeg;index<num;index++) {
0615 line_t& item = m_lines[index];
0616 size_t pos = item.first;
0617 size_t npt = item.second;
0618 float* data = m_xys.data()+pos;
0619 for(size_t i=0;i<npt;i++,data+=2) *data -= 0.5F * sx;
0620 }
0621 } else if(hjust==tools::sg::right) {
0622 size_t num = m_lines.size();
0623 for(size_t index=ibeg;index<num;index++) {
0624 line_t& item = m_lines[index];
0625 size_t pos = item.first;
0626 size_t npt = item.second;
0627 float* data = m_xys.data()+pos;
0628 for(size_t i=0;i<npt;i++,data+=2) *data -= sx;
0629 }
0630 }}
0631 }
0632
0633 } else if(unitext.values().size()) {
0634 float yline = 0;
0635 tools_vforcit(uniline,unitext.values(),vit) {
0636 const uniline& line = *vit;
0637 m_trans_x = 0;
0638 m_trans_y = yline;
0639 size_t ibeg = m_lines.size(); //for hjust.
0640 tools_vforcit(unichar,line,it) {
0641 if(!char_outline_2_gl(a_out,*it)) return;
0642 }
0643 yline += -float(face_height)*m_scale; //height>0
0644
0645 {float sx = m_trans_x;
0646 if(hjust==tools::sg::center) {
0647 size_t num = m_lines.size();
0648 for(size_t index=ibeg;index<num;index++) {
0649 line_t& item = m_lines[index];
0650 size_t pos = item.first;
0651 size_t npt = item.second;
0652 float* data = m_xys.data()+pos;
0653 for(size_t i=0;i<npt;i++,data+=2) *data -= 0.5F*sx;
0654 }
0655 } else if(hjust==tools::sg::center) {
0656 size_t num = m_lines.size();
0657 for(size_t index=ibeg;index<num;index++) {
0658 line_t& item = m_lines[index];
0659 size_t pos = item.first;
0660 size_t npt = item.second;
0661 float* data = m_xys.data()+pos;
0662 for(size_t i=0;i<npt;i++,data+=2) *data -= sx;
0663 }
0664 }}
0665 }
0666 }
0667 }
0668
0669 bool char_outline_2_gl(std::ostream& a_out,unsigned int a_unichar) {
0670 FT_ULong charcode = a_unichar; //charcode is UTF-32.
0671 FT_UInt glyph_index = ::FT_Get_Char_Index(m_face,charcode);
0672 //NOTE : if not found -> glyph_index = 0 which is the "missing glyph".
0673 if((FT_Long)glyph_index>=m_face->num_glyphs) {
0674 a_out << "toolx::sg::text_freetype::char_outline_2_gl :"
0675 << " FT_Get_Char_Index : failed for char : " << a_unichar
0676 << std::endl;
0677 ::FT_Done_Face(m_face);
0678 m_face = 0;
0679 return false;
0680 }
0681
0682 {FT_Error error = ::FT_Load_Glyph(m_face,glyph_index,load_flags());
0683 if(error) {
0684 a_out << "toolx::sg::text_freetype::char_outline_2_gl :"
0685 << " for character " << a_unichar
0686 << ",FT_Load_Glyph : error : " << serror(error)
0687 << std::endl;
0688 ::FT_Done_Face(m_face);
0689 m_face = 0;
0690 return false;
0691 }}
0692
0693 //FT_GlyphSlot FT_Face.glyph;
0694 if(m_face->glyph->format!=FT_GLYPH_FORMAT_OUTLINE) {
0695 a_out << "toolx::sg::text_freetype::char_outline_2_gl :"
0696 << " for font " << tools::sout(font.value())
0697 << " and for character " << a_unichar
0698 << " glyph not at format outline."
0699 << std::endl;
0700 ::FT_Done_Face(m_face);
0701 m_face = 0;
0702 return false;
0703 }
0704
0705 FT_Outline outline = m_face->glyph->outline;
0706
0707 {FT_Error error = ::FT_Outline_Decompose(&outline,&m_funcs,this);
0708 if(error) {
0709 a_out << "toolx::sg::text_freetype::char_outline_2_gl :"
0710 << " for character " << a_unichar
0711 << ",FT_Outline_Decompose : error : " << serror(error)
0712 << std::endl;
0713 ::FT_Done_Face(m_face);
0714 m_face = 0;
0715 return false;
0716 }}
0717
0718 flush_line();
0719
0720 m_trans_x += float(m_face->glyph->advance.x)*m_scale;
0721 m_trans_y += float(m_face->glyph->advance.y)*m_scale;
0722
0723 {wndg_type wdg = ((outline.flags & FT_OUTLINE_REVERSE_FILL)?wndg_ccw:wndg_cw);
0724 if(m_wndg==wndg_not_done) {
0725 m_wndg = wdg;
0726 } else if(m_wndg!=wdg) {
0727 a_out << "toolx::sg::text_freetype::char_outline_2_gl :"
0728 << " for character " << a_unichar << ", winding anomaly."
0729 << std::endl;
0730 }}
0731
0732 return true;
0733 }
0734
0735 protected:
0736 void flush_line(){
0737 size_t num = (m_xys.size()-m_pos)/2;
0738 if(num) {
0739 m_lines.push_back(line_t(m_pos,num));
0740 }
0741 m_pos = m_xys.size();
0742 }
0743
0744 ////////////////////////////////////////////////////////
0745 /// outline triangles //////////////////////////////////
0746 ////////////////////////////////////////////////////////
0747 void outline_triangles_2_gl(std::ostream& a_out) {
0748 if(!set_char_size(a_out,m_face,height.value(),m_scale)) return;
0749 FT_Pos face_height = m_face->size->metrics.height; //FT_Pos (long)
0750
0751 m_tobj = gluNewTess();
0752
0753 // NOTE : the gluTessCallback_<enum>() functions are tools/glutess specific.
0754 // They had been introduced to avoid g++-8.1.0 warnings :
0755 // warnings : cast between incompatible function types.
0756 ::gluTessCallback_GLU_TESS_BEGIN_DATA (m_tobj,begin_cbk);
0757 ::gluTessCallback_GLU_TESS_END_DATA (m_tobj,end_cbk);
0758 ::gluTessCallback_GLU_TESS_VERTEX_DATA (m_tobj,vertex_cbk);
0759 ::gluTessCallback_GLU_TESS_COMBINE_DATA (m_tobj,combine_cbk);
0760 ::gluTessCallback_GLU_TESS_ERROR_DATA (m_tobj,error_cbk);
0761
0762 ::gluTessProperty(m_tobj,(GLUenum)GLU_TESS_WINDING_RULE,GLU_TESS_WINDING_ODD);
0763
0764 if(strings.values().size()) {
0765 float yline = 0;
0766 tools_vforcit(std::string,strings.values(),vit) {
0767 const std::string& line = *vit;
0768 m_trans_x = 0;
0769 m_trans_y = yline;
0770 size_t ibeg = m_triangles.size(); //for hjust.
0771 for(auto it = line.cbegin(); it != line.cend(); ++it) {
0772 if(!char_triangles_2_gl(a_out,*it + m_encoding_offset)) {
0773 ::gluDeleteTess(m_tobj);
0774 m_tobj = 0;
0775 return;
0776 }
0777 }
0778 yline += -float(face_height)*m_scale; //height>0
0779
0780 {float sx = m_trans_x;
0781 if(hjust==tools::sg::center) {
0782 size_t num = m_triangles.size();
0783 for(size_t index=ibeg;index<num;index++) {
0784 std::pair<GLUenum,triangle_t>& item = m_triangles[index];
0785 size_t pos = item.second.first;
0786 size_t npt = item.second.second;
0787 float* data = m_xys.data()+pos;
0788 for(size_t i=0;i<npt;i++,data+=2) *data -= sx*0.5f;
0789 }
0790 } else if(hjust==tools::sg::right) {
0791 size_t num = m_triangles.size();
0792 for(size_t index=ibeg;index<num;index++) {
0793 std::pair<GLUenum,triangle_t>& item = m_triangles[index];
0794 size_t pos = item.second.first;
0795 size_t npt = item.second.second;
0796 float* data = m_xys.data()+pos;
0797 for(size_t i=0;i<npt;i++,data+=2) *data -= sx;
0798 }
0799 }}
0800 }
0801 } else if(unitext.values().size()) {
0802 float yline = 0;
0803 tools_vforcit(uniline,unitext.values(),vit) {
0804 const uniline& line = *vit;
0805 m_trans_x = 0;
0806 m_trans_y = yline;
0807 size_t ibeg = m_triangles.size(); //for hjust.
0808 tools_vforcit(unichar,line,it) {
0809 if(!char_triangles_2_gl(a_out,*it)) {
0810 ::gluDeleteTess(m_tobj);
0811 m_tobj = 0;
0812 return;
0813 }
0814 }
0815 yline += -float(face_height)*m_scale; //height>0
0816
0817 {float sx = m_trans_x;
0818 if(hjust==tools::sg::center) {
0819 size_t num = m_triangles.size();
0820 for(size_t index=ibeg;index<num;index++) {
0821 std::pair<GLUenum,triangle_t>& item = m_triangles[index];
0822 size_t pos = item.second.first;
0823 size_t npt = item.second.second;
0824 float* data = m_xys.data()+pos;
0825 for(size_t i=0;i<npt;i++,data+=2) *data -= sx*0.5f;
0826 }
0827 } else if(hjust==tools::sg::right) {
0828 size_t num = m_triangles.size();
0829 for(size_t index=ibeg;index<num;index++) {
0830 std::pair<GLUenum,triangle_t>& item = m_triangles[index];
0831 size_t pos = item.second.first;
0832 size_t npt = item.second.second;
0833 float* data = m_xys.data()+pos;
0834 for(size_t i=0;i<npt;i++,data+=2) *data -= sx;
0835 }
0836 }}
0837 }
0838 }
0839
0840 ::gluDeleteTess(m_tobj);
0841 m_tobj = 0;
0842 }
0843
0844 bool char_triangles_2_gl(std::ostream& a_out,unsigned int a_unichar) {
0845 FT_ULong charcode = a_unichar; //charcode is UTF-32.
0846 FT_UInt glyph_index = ::FT_Get_Char_Index(m_face,charcode);
0847 //NOTE : if not found -> glyph_index = 0 which is the "missing glyph".
0848 if((FT_Long)glyph_index>=m_face->num_glyphs) {
0849 a_out << "toolx::sg::text_freetype::char_triangles_2_gl :"
0850 << " FT_Get_Char_Index : failed for char : " << a_unichar
0851 << std::endl;
0852 ::FT_Done_Face(m_face);
0853 m_face = 0;
0854 return false;
0855 }
0856
0857 {FT_Error error = ::FT_Load_Glyph(m_face,glyph_index,load_flags());
0858 if(error) {
0859 a_out << "toolx::sg::text_freetype::char_triangles_2_gl :"
0860 << " for character " << a_unichar
0861 << ",FT_Load_Glyph : error : " << serror(error)
0862 << std::endl;
0863 ::FT_Done_Face(m_face);
0864 m_face = 0;
0865 return false;
0866 }}
0867
0868 //FT_GlyphSlot FT_Face.glyph;
0869 if(m_face->glyph->format!=FT_GLYPH_FORMAT_OUTLINE) {
0870 a_out << "toolx::sg::text_freetype::char_triangles_2_gl :"
0871 << " for font " << tools::sout(font.value())
0872 << " and for character " << a_unichar
0873 << " glyph not at format outline."
0874 << std::endl;
0875 ::FT_Done_Face(m_face);
0876 m_face = 0;
0877 return false;
0878 }
0879
0880 FT_Outline outline = m_face->glyph->outline;
0881
0882 m_glutess_trids_num = 0;
0883 m_combine_trids_num = 0;
0884
0885 m_contour_open = false;
0886
0887 ::gluTessBeginPolygon(m_tobj,this);
0888
0889 {FT_Error error = ::FT_Outline_Decompose(&outline,&m_funcs,this);
0890 if(error) {
0891 a_out << "toolx::sg::text_freetype::char_triangles_2_gl :"
0892 << " for character " << a_unichar
0893 << ",FT_Outline_Decompose : error : " << serror(error)
0894 << std::endl;
0895 ::FT_Done_Face(m_face);
0896 m_face = 0;
0897 return false;
0898 }}
0899
0900 if(m_contour_open) {
0901 ::gluTessEndContour(m_tobj);
0902 m_contour_open = false;
0903 }
0904
0905 ::gluTessEndPolygon(m_tobj); //triggers callbacks and fill m_triangles.
0906
0907 m_trans_x += float(m_face->glyph->advance.x)*m_scale;
0908 m_trans_y += float(m_face->glyph->advance.y)*m_scale;
0909
0910 {wndg_type wdg = ((outline.flags & FT_OUTLINE_REVERSE_FILL)?wndg_ccw:wndg_cw);
0911 if(m_wndg==wndg_not_done) {
0912 m_wndg = wdg;
0913 } else if(m_wndg!=wdg) {
0914 a_out << "toolx::sg::text_freetype::char_triangles_2_gl :"
0915 << " for character " << a_unichar << ", winding anomaly."
0916 << std::endl;
0917 }}
0918
0919 return true;
0920 }
0921
0922 ////////////////////////////////////////////////////////
0923 /// bitmap /////////////////////////////////////////////
0924 ////////////////////////////////////////////////////////
0925 bool bitmap_2_gl(std::ostream& a_out) {
0926 FT_F26Dot6 wchar = (FT_F26Dot6)(m_char_height*64);
0927 FT_F26Dot6 hchar = (FT_F26Dot6)(m_char_height*64);
0928 FT_UInt hres = 72;
0929 FT_UInt vres = 72;
0930 FT_Error error = ::FT_Set_Char_Size(m_face,wchar,hchar,hres,vres);
0931 if(error) {
0932 a_out << "toolx::sg::text_freetype::bitmap_2_gl :"
0933 << " FT_Set_Char_Size : error : " << serror(error) << "."
0934 << std::endl;
0935 ::FT_Done_Face(m_face);
0936 m_face = 0;
0937 return false;
0938 }
0939 m_scale = height.value()/float(hchar);
0940
0941 FT_Pos face_height = m_face->size->metrics.height; //FT_Pos (long)
0942 if(strings.values().size()) {
0943 float yline = 0;
0944 tools_vforcit(std::string,strings.values(),vit) {
0945 const std::string& line = *vit;
0946 m_trans_x = 0;
0947 m_trans_y = yline;
0948 size_t ibeg = m_tqs.size(); //for hjust.
0949 for(auto it = line.cbegin(); it != line.cend(); ++it) {
0950 if(!char_2_bitmap(a_out,*it + m_encoding_offset)) return false;
0951 }
0952 yline += -float(face_height)*m_scale; //height>0
0953
0954 {float sx = m_trans_x;
0955 if(hjust==tools::sg::center) {
0956 size_t num = m_tqs.size();
0957 for(size_t index=ibeg;index<num;index++) {
0958 std::vector<tools::vec3f>& vcs = m_tqs[index]->corners.values();
0959 for(size_t i=0;i<4;i++) vcs[i][0] -= sx*0.5f;
0960 }
0961 } else if(hjust==tools::sg::right) {
0962 size_t num = m_tqs.size();
0963 for(size_t index=ibeg;index<num;index++) {
0964 std::vector<tools::vec3f>& vcs = m_tqs[index]->corners.values();
0965 for(size_t i=0;i<4;i++) vcs[i][0] -= sx;
0966 }
0967 }}
0968 }
0969 } else if(unitext.values().size()) {
0970 float yline = 0;
0971 tools_vforcit(uniline,unitext.values(),vit) {
0972 const uniline& line = *vit;
0973 m_trans_x = 0;
0974 m_trans_y = yline;
0975 size_t ibeg = m_tqs.size(); //for hjust.
0976 tools_vforcit(unichar,line,it) {
0977 if(!char_2_bitmap(a_out,*it)) return false;
0978 }
0979 yline += -float(face_height)*m_scale; //height>0
0980
0981 {float sx = m_trans_x;
0982 if(hjust==tools::sg::center) {
0983 size_t num = m_tqs.size();
0984 for(size_t index=ibeg;index<num;index++) {
0985 std::vector<tools::vec3f>& vcs = m_tqs[index]->corners.values();
0986 for(size_t i=0;i<4;i++) vcs[i][0] -= sx*0.5f;
0987 }
0988 } else if(hjust==tools::sg::right) {
0989 size_t num = m_tqs.size();
0990 for(size_t index=ibeg;index<num;index++) {
0991 std::vector<tools::vec3f>& vcs = m_tqs[index]->corners.values();
0992 for(size_t i=0;i<4;i++) vcs[i][0] -= sx;
0993 }
0994 }}
0995 }
0996 }
0997 return true;
0998 }
0999
1000 bool char_2_bitmap(std::ostream& a_out,unsigned int a_unichar) {
1001 FT_ULong charcode = a_unichar; //charcode is UTF-32.
1002 FT_UInt glyph_index = ::FT_Get_Char_Index(m_face,charcode);
1003 //NOTE : if not found -> glyph_index = 0 which is the "missing glyph".
1004 if((FT_Long)glyph_index>=m_face->num_glyphs) {
1005 a_out << "toolx::sg::text_freetype::char_2_bitmap :"
1006 << " FT_Get_Char_Index : failed for char : " << a_unichar
1007 << std::endl;
1008 ::FT_Done_Face(m_face);
1009 m_face = 0;
1010 return false;
1011 }
1012
1013 {FT_Error error = ::FT_Load_Glyph(m_face,glyph_index,load_flags());
1014 if(error) {
1015 a_out << "toolx::sg::text_freetype::char_2_bitmap :"
1016 << " for character " << a_unichar
1017 << ",FT_Load_Glyph : error : " << serror(error)
1018 << std::endl;
1019 ::FT_Done_Face(m_face);
1020 m_face = 0;
1021 return false;
1022 }}
1023
1024 FT_Glyph glyph;
1025 {FT_Error error = ::FT_Get_Glyph(m_face->glyph,&glyph);
1026 if (error) {
1027 a_out << "toolx::sg::text_freetype::char_2_bitmap :"
1028 << " for font " << tools::sout(font.value())
1029 << " and for character " << a_unichar
1030 << " could not get glyph."
1031 << std::endl;
1032 ::FT_Done_Face(m_face);
1033 m_face = 0;
1034 return false;
1035 }}
1036
1037 bool smoothing = true;
1038 {FT_Error error = ::FT_Glyph_To_Bitmap(&glyph,(smoothing?ft_render_mode_normal:ft_render_mode_mono),0,1);
1039 if (error) {
1040 a_out << "toolx::sg::text_freetype::char_2_bitmap :"
1041 << " for font " << tools::sout(font.value())
1042 << " and for character " << a_unichar
1043 << " could not get glyph bitmap."
1044 << std::endl;
1045 ::FT_Done_Glyph(glyph);
1046 ::FT_Done_Face(m_face);
1047 m_face = 0;
1048 return false;
1049 }}
1050
1051 FT_BitmapGlyph bitmap = (FT_BitmapGlyph)glyph;
1052
1053 if( (bitmap->bitmap.pixel_mode!=ft_pixel_mode_mono) &&
1054 (bitmap->bitmap.pixel_mode!=ft_pixel_mode_grays) ){
1055 a_out << "toolx::sg::text_freetype::char_2_bitmap :"
1056 << " for font " << tools::sout(font.value())
1057 << " and for character " << a_unichar
1058 << " not a mono or grays pixmap."
1059 << std::endl;
1060 ::FT_Done_Glyph(glyph);
1061 ::FT_Done_Face(m_face);
1062 m_face = 0;
1063 return false;
1064 }
1065
1066 if(bitmap->bitmap.pitch<0) {
1067 a_out << "toolx::sg::text_freetype::char_2_bitmap :"
1068 << " for font " << tools::sout(font.value())
1069 << " and for character " << a_unichar
1070 << " negative bitmap pitch."
1071 << std::endl;
1072 ::FT_Done_Glyph(glyph);
1073 ::FT_Done_Face(m_face);
1074 m_face = 0;
1075 return false;
1076 }
1077
1078 unsigned int img_w = 0;
1079 unsigned int img_h = 0;
1080 unsigned int img_bpp = 4;
1081 size_t img_sz = 0;
1082
1083 if(bitmap->bitmap.pixel_mode==ft_pixel_mode_mono) {
1084 a_out << "toolx::sg::text_freetype::char_2_bitmap : mode_mono : not yet handled." << std::endl;
1085
1086 img_h = bitmap->bitmap.rows;
1087 //WARNING : bitmap->bitmap.pitch != int((bitmap->bitmap.width+7)/8) !!!
1088 // OpenGL wants the below for cols.
1089 img_w = (bitmap->bitmap.width+7)/8;
1090 img_sz = img_w * img_h;
1091
1092 ::FT_Done_Glyph(glyph);
1093 ::FT_Done_Face(m_face);
1094 m_face = 0;
1095 return false;
1096
1097 } else { //ft_pixel_mode_grays
1098 if(int(bitmap->bitmap.width)!=bitmap->bitmap.pitch) {
1099 a_out << "toolx::sg::text_freetype::char_2_bitmap :"
1100 << " for font " << tools::sout(font.value())
1101 << " and for character " << a_unichar
1102 << "bitmap pitch (" << bitmap->bitmap.pitch << ") != width (" << bitmap->bitmap.width << ")."
1103 << std::endl;
1104 ::FT_Done_Glyph(glyph);
1105 ::FT_Done_Face(m_face);
1106 m_face = 0;
1107 return false;
1108 }
1109 img_w = bitmap->bitmap.width;
1110 img_h = bitmap->bitmap.rows;
1111 img_sz = img_w * img_h * img_bpp;
1112 }
1113
1114 if(img_sz<=0) {
1115 // This may happen (for example for the space character).
1116 } else {
1117 tools::byte* img_buffer = new tools::byte[img_sz];
1118 if(!img_buffer) {
1119 a_out << "toolx::sg::text_freetype::char_2_bitmap :"
1120 << " for font " << tools::sout(font.value())
1121 << " and for character " << a_unichar
1122 << ", can't alloc bitmap buffer for character."
1123 << std::endl;
1124 ::FT_Done_Glyph(glyph);
1125 ::FT_Done_Face(m_face);
1126 m_face = 0;
1127 return false;
1128 }
1129 // The bitmap is upside down for OpenGL.
1130 float a,b;
1131 tools::colorf back_color = tools::colorf_white();
1132 back_color.set_a(0); //transparent background.
1133 typedef unsigned char uchar;
1134 for(unsigned int row=0;row<img_h;++row) {
1135 unsigned char* from = (unsigned char*)bitmap->bitmap.buffer + (bitmap->bitmap.rows-row-1)*bitmap->bitmap.pitch;
1136 unsigned char* to = img_buffer + row * img_w * img_bpp;
1137 for(unsigned int col=0;col<img_w;++col,++from) {
1138 a = float(*from)/255.0f;
1139 b = float(255-*from)/255.0f;
1140 *to = uchar(m_front_color.ruchar()*a+back_color.ruchar()*b);to++;
1141 *to = uchar(m_front_color.guchar()*a+back_color.guchar()*b);to++;
1142 *to = uchar(m_front_color.buchar()*a+back_color.buchar()*b);to++;
1143 *to = uchar(m_front_color.auchar()*a+back_color.auchar()*b);to++;
1144 }
1145 }
1146
1147 FT_BBox _bbox;
1148 FT_Glyph_Get_CBox(glyph,ft_glyph_bbox_pixels,&_bbox);
1149
1150 std::vector<tools::vec3f> vcs;
1151 float scale = height.value()/m_char_height;
1152 float x_min = float(_bbox.xMin)*scale+m_trans_x;
1153 float x_max = float(_bbox.xMax)*scale+m_trans_x;
1154 float y_min = float(_bbox.yMin)*scale+m_trans_y;
1155 float y_max = float(_bbox.yMax)*scale+m_trans_y;
1156
1157 vcs.push_back(tools::vec3f(x_min,y_min,0));
1158 vcs.push_back(tools::vec3f(x_max,y_min,0));
1159 vcs.push_back(tools::vec3f(x_max,y_max,0));
1160 vcs.push_back(tools::vec3f(x_min,y_max,0));
1161
1162 tools::sg::tex_quadrilateral* _node = new tools::sg::tex_quadrilateral;
1163 _node->img.value().set(img_w,img_h,img_bpp,img_buffer,true);
1164 _node->expand = true;
1165 _node->back_color = tools::colorf_white(); //used as back pixel when expanding.
1166 _node->back_color.value().set_a(0); //transparent background.
1167 //_node->nearest = false; //to have antialiasing on texture.
1168 _node->corners.set_values(vcs);
1169
1170 m_bitmaps.add(_node);
1171 m_tqs.push_back(_node);
1172 }
1173
1174 m_trans_x += float(m_face->glyph->advance.x)*m_scale;
1175 m_trans_y += float(m_face->glyph->advance.y)*m_scale;
1176
1177 ::FT_Done_Glyph(glyph);
1178
1179 return true;
1180 }
1181
1182 typedef _GLUfuncptr Func;
1183
1184 static void GLUAPIENTRY begin_cbk(GLUenum a_which,void* a_this) {
1185 text_freetype& self = *((text_freetype*)a_this);
1186 self.m_mode = a_which;
1187 self.m_pos = self.m_xys.size();
1188 }
1189
1190 static void GLUAPIENTRY vertex_cbk(void* a_vertex,void* a_this) {
1191 text_freetype& self = *((text_freetype*)a_this);
1192 double* vertex = (double*)a_vertex;
1193 self.add_xy(float(vertex[0]),float(vertex[1]));
1194 }
1195
1196 static void GLUAPIENTRY end_cbk(void* a_this){
1197 text_freetype& self = *((text_freetype*)a_this);
1198 size_t num = (self.m_xys.size()-self.m_pos)/2;
1199 if(num) {
1200 triangle_t t(self.m_pos,num);
1201 self.m_triangles.push_back(std::pair<GLUenum,triangle_t>(self.m_mode,t));
1202 }
1203 }
1204
1205 static void GLUAPIENTRY combine_cbk(double a_coords[3],
1206 void* /*a_vertex_data*/[4],
1207 float /*a_weight*/[4],
1208 void** a_data_out,
1209 void* a_this) {
1210 text_freetype& self = *((text_freetype*)a_this);
1211 double* v = self.add_combine_vec3d(a_coords[0],a_coords[1],a_coords[2]);
1212 //if(!v) ???
1213 *a_data_out = v;
1214 }
1215
1216 static void GLUAPIENTRY error_cbk(GLUenum,void*) {
1217 }
1218
1219 ////////////////////////////////////////////////////////
1220 /// outline triangles : end ////////////////////////////
1221 ////////////////////////////////////////////////////////
1222
1223 ////////////////////////////////////////////////////////
1224 /// FT_Outline_Decompose callbacks /////////////////////
1225 ////////////////////////////////////////////////////////
1226 static int outline_move_to(const FT_Vector* a_to,void* a_this){
1227 // NOTE : get x coords in units of wchar,
1228 // get y coords in units of hchar.
1229 // Exa : if char_width is 100*64 get some x of
1230 // the same magnitude (in [0,6400]).
1231 text_freetype& self = *((text_freetype*)a_this);
1232
1233 float gx,gy;
1234 self.set_g(gx,gy,float(a_to->x),float(a_to->y));
1235
1236 if(self.m_tobj) {
1237 if(self.m_contour_open) {
1238 ::gluTessEndContour(self.m_tobj);
1239 self.m_contour_open = false;
1240 }
1241
1242 ::gluTessBeginContour(self.m_tobj);
1243 self.m_contour_open = true;
1244
1245 {double* v = self.add_glutess_vec3d(gx,gy,0);
1246 ::gluTessVertex(self.m_tobj,v,v);}
1247
1248 } else {
1249 self.flush_line();
1250 self.add_xy(gx,gy);
1251 }
1252
1253 self.m_last_x = float(a_to->x);
1254 self.m_last_y = float(a_to->y);
1255
1256 return 0;
1257 }
1258 static int outline_line_to(const FT_Vector* a_to,void* a_this){
1259 text_freetype& self = *((text_freetype*)a_this);
1260 float gx,gy;
1261 self.set_g(gx,gy,float(a_to->x),float(a_to->y));
1262
1263 if(self.m_tobj) {
1264 double* v = self.add_glutess_vec3d(gx,gy,0);
1265 ::gluTessVertex(self.m_tobj,v,v);
1266 } else {
1267 self.add_xy(gx,gy);
1268 }
1269
1270 self.m_last_x = float(a_to->x);
1271 self.m_last_y = float(a_to->y);
1272
1273 return 0;
1274 }
1275 static int outline_conic_to(const FT_Vector* a_ctrl,const FT_Vector* a_to,void* a_this){
1276 text_freetype& self = *((text_freetype*)a_this);
1277
1278 // it must be fast. We avoid vec3f manipulations.
1279
1280 //self.m_out << "toolx::sg::text_freetype::outline_conic_to :"
1281 // << " ctrl x " << a_ctrl->x
1282 // << " ctrl y " << a_ctrl->y
1283 // << " x " << a_to->x
1284 // << " y " << a_to->y
1285 // << std::endl;
1286
1287 float ctrlx = float(a_ctrl->x);
1288 float ctrly = float(a_ctrl->y);
1289
1290 float fromx = self.m_last_x;
1291 float fromy = self.m_last_y;
1292
1293 float tox = float(a_to->x);
1294 float toy = float(a_to->y);
1295
1296 // logic taken from OGLFT.
1297
1298 float bx = fromx - 2 * ctrlx + tox;
1299 float by = fromy - 2 * ctrly + toy;
1300
1301 float cx = -2 * fromx + 2 * ctrlx;
1302 float cy = -2 * fromy + 2 * ctrly;
1303
1304 float dfx = cx * self.m_delta + bx * self.m_delta2;
1305 float dfy = cy * self.m_delta + by * self.m_delta2;
1306
1307 float df2x = 2 * bx * self.m_delta2;
1308 float df2y = 2 * by * self.m_delta2;
1309
1310 // if steps = 4, num = 3
1311 // from (i=0) (i=1) (i=2) (to)
1312 // then we have four steps between [from,to]
1313
1314 // from = starting point.
1315
1316 float gx,gy;
1317
1318 size_t num = self.m_steps - 1;
1319 for(size_t i=0;i<num;i++) {
1320 fromx += dfx;
1321 fromy += dfy;
1322
1323 self.set_g(gx,gy,fromx,fromy);
1324
1325 if(self.m_tobj) {
1326 double* v = self.add_glutess_vec3d(gx,gy,0);
1327 ::gluTessVertex(self.m_tobj,v,v);
1328 } else {
1329 self.add_xy(gx,gy);
1330 }
1331
1332 dfx += df2x;
1333 dfy += df2y;
1334 }
1335
1336 self.set_g(gx,gy,tox,toy);
1337
1338 if(self.m_tobj) {
1339 double* v = self.add_glutess_vec3d(gx,gy,0);
1340 ::gluTessVertex(self.m_tobj,v,v);
1341 } else {
1342 self.add_xy(gx,gy);
1343 }
1344
1345 //self.m_last = to;
1346 self.m_last_x = tox;
1347 self.m_last_y = toy;
1348
1349 return 0;
1350 }
1351 void set_g(float& a_gx,float& a_gy,float a_x,float a_y) const {
1352 a_gx = a_x*m_scale+m_trans_x;
1353 a_gy = a_y*m_scale+m_trans_y;
1354 }
1355 static int outline_cubic_to(const FT_Vector* a_ctrl1,const FT_Vector* a_ctrl2,const FT_Vector* a_to,void* a_this){
1356 text_freetype& self = *((text_freetype*)a_this);
1357
1358 // it must be fast. We avoid vec3f manipulations.
1359
1360 float ctrl1x = float(a_ctrl1->x);
1361 float ctrl1y = float(a_ctrl1->y);
1362
1363 float ctrl2x = float(a_ctrl2->x);
1364 float ctrl2y = float(a_ctrl2->y);
1365
1366 float fromx = self.m_last_x;
1367 float fromy = self.m_last_y;
1368
1369 float tox = float(a_to->x);
1370 float toy = float(a_to->y);
1371
1372 // logic taken from OGLFT.
1373
1374 float ax = -fromx + 3 * ctrl1x - 3 * ctrl2x + tox;
1375 float ay = -fromy + 3 * ctrl1y - 3 * ctrl2y + toy;
1376
1377 float bx = 3 * fromx - 6 * ctrl1x + 3 * ctrl2x;
1378 float by = 3 * fromy - 6 * ctrl1y + 3 * ctrl2y;
1379
1380 float cx = -3 * fromx + 3 * ctrl1x;
1381 float cy = -3 * fromy + 3 * ctrl1y;
1382
1383 float dfx = cx * self.m_delta + bx * self.m_delta2 + ax * self.m_delta3;
1384 float dfy = cy * self.m_delta + by * self.m_delta2 + ay * self.m_delta3;
1385
1386 float df2x = 2 * bx * self.m_delta2 + 6 * ax * self.m_delta3;
1387 float df2y = 2 * by * self.m_delta2 + 6 * ay * self.m_delta3;
1388
1389 float df3x = 6 * ax * self.m_delta3;
1390 float df3y = 6 * ay * self.m_delta3;
1391
1392 // if steps = 4, num = 3
1393 // from (i=0) (i=1) (i=2) (to)
1394 // then we have four steps between [from,to]
1395
1396 // from = starting point.
1397 float gx,gy;
1398
1399 size_t num = self.m_steps - 1;
1400 for(size_t i=0;i<num;i++) {
1401 fromx += dfx;
1402 fromy += dfy;
1403
1404 self.set_g(gx,gy,fromx,fromy);
1405
1406 if(self.m_tobj) {
1407 double* v = self.add_glutess_vec3d(gx,gy,0);
1408 ::gluTessVertex(self.m_tobj,v,v);
1409 } else {
1410 self.add_xy(gx,gy);
1411 }
1412
1413 dfx += df2x;
1414 dfy += df2y;
1415
1416 df2x += df3x;
1417 df2y += df3y;
1418 }
1419
1420 //g = to;
1421 self.set_g(gx,gy,tox,toy);
1422
1423 if(self.m_tobj) {
1424 double* v = self.add_glutess_vec3d(gx,gy,0);
1425 ::gluTessVertex(self.m_tobj,v,v);
1426 } else {
1427 self.add_xy(gx,gy);
1428 }
1429
1430 //self.m_last = to;
1431 self.m_last_x = tox;
1432 self.m_last_y = toy;
1433
1434 return 0;
1435 }
1436
1437
1438 public:
1439 #ifndef SWIG
1440 class serrors : public std::map<int,std::string> {
1441 typedef std::map<int,std::string> parent;
1442 public:
1443 serrors(){
1444 #undef __FTERRORS_H__
1445 #define FT_ERROR_START_LIST
1446 #define FT_ERROR_END_LIST
1447 #define FT_ERRORDEF( e, v, s ) parent::operator[](e) = s;
1448 #include FT_ERRORS_H
1449 }
1450 virtual ~serrors() {}
1451 protected:
1452 serrors(const serrors& a_from):parent(a_from) {}
1453 serrors& operator=(const serrors&){return *this;}
1454 };
1455 static std::string serror(int a_FT_Error) {
1456 static const serrors errs;
1457 std::map<int,std::string>::const_iterator it = errs.find(a_FT_Error);
1458 if(it!=errs.end()) return (*it).second;
1459 return "unknown";
1460 }
1461 #endif
1462 protected:
1463 void add_xy(float a_x,float a_y) {
1464 m_xys.push_back(a_x);
1465 m_xys.push_back(a_y);
1466 }
1467
1468 double* add_glutess_vec3d(float a_x,float a_y,float a_z) {
1469 double* v = 0;
1470 if(m_glutess_trids_num>=m_glutess_trids.size()) {
1471 v = new double[3];
1472 m_glutess_trids.push_back(v);
1473 } else {
1474 v = m_glutess_trids[m_glutess_trids_num];
1475 }
1476 m_glutess_trids_num++;
1477
1478 v[0] = a_x;
1479 v[1] = a_y;
1480 v[2] = a_z;
1481
1482 return v;
1483 }
1484
1485 double* add_combine_vec3d(double a_x,double a_y,double a_z) {
1486 double* v = 0;
1487 if(m_combine_trids_num>=m_combine_trids.size()) {
1488 v = new double[3];
1489 m_combine_trids.push_back(v);
1490 } else {
1491 v = m_combine_trids[m_combine_trids_num];
1492 }
1493 m_combine_trids_num++;
1494
1495 v[0] = a_x;
1496 v[1] = a_y;
1497 v[2] = a_z;
1498
1499 return v;
1500 }
1501
1502 void clear_trids() {
1503 {tools_vforit(double*,m_glutess_trids,it) delete [] *it;
1504 m_glutess_trids.clear();}
1505
1506 {tools_vforit(double*,m_combine_trids,it) delete [] *it;
1507 m_combine_trids.clear();}
1508 }
1509
1510 void load_face(std::ostream& a_out) {
1511 if(!m_library) {
1512 a_out << "toolx::sg::text_freetype::load_face :"
1513 << " freetype library not initialized."
1514 << std::endl;
1515 return;
1516 }
1517
1518 if(m_verbose) {
1519 a_out << "toolx::sg::text_freetype::load_face :"
1520 << " font is " << tools::sout(font.value()) << "."
1521 << std::endl;
1522 }
1523
1524 if(m_face) {
1525 ::FT_Done_Face(m_face);
1526 m_face = 0;
1527 }
1528
1529 if(font.value().empty()) {
1530 a_out << "toolx::sg::text_freetype::load_face :"
1531 << " no font given."
1532 << std::endl;
1533 return;
1534 }
1535
1536 // look for embedded fonts:
1537 {unsigned int size;
1538 const unsigned char* buffer;
1539 if(parent::find_embedded_font(font.value(),size,buffer)) {
1540 FT_Error error =
1541 ::FT_New_Memory_Face(m_library,(const FT_Byte*)buffer,(FT_Long)size,0,&m_face);
1542 if(error) {
1543 a_out << "toolx::sg::text_freetype::load_face :"
1544 << " FT_New_Memory_Face : error : " << serror(error) << "."
1545 << std::endl;
1546 m_face = 0;
1547 return;
1548 }
1549 if(m_verbose) a_out << "toolx::sg::text_freetype::load_face : load embedded font ok." << std::endl;
1550 return;
1551 }}
1552
1553 std::string file;
1554
1555 if(parent::find_font_with_finders(font.value(),file)) {
1556 } else {
1557 tools::file::find_with_env(a_out,s_TOOLS_FONT_PATH(),font.value(),file,false);
1558 }
1559
1560 if(file.empty()) {
1561 a_out << "toolx::sg::text_freetype::load_face :"
1562 << " font file not found for font "
1563 << tools::sout(font.value()) << "."
1564 << std::endl;
1565 return;
1566 }
1567
1568 if(m_verbose) {
1569 a_out << "toolx::sg::text_freetype::load_face :"
1570 << " load font file " << tools::sout(file) << " ..."
1571 << std::endl;
1572 }
1573
1574 FT_Error error = ::FT_New_Face(m_library,file.c_str(),0,&m_face);
1575 if(error) {
1576 a_out << "toolx::sg::text_freetype::load_face :"
1577 << " FT_New_Face : error : " << serror(error) << "."
1578 << " for font file " << tools::sout(file) << "."
1579 << std::endl;
1580 m_face = 0;
1581 return;
1582 }
1583
1584 if(m_verbose) {
1585 a_out << "toolx::sg::text_freetype::load_face :"
1586 << " load ok."
1587 << std::endl;
1588 }
1589
1590 }
1591
1592 protected:
1593 static int load_flags() {
1594 return FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING;
1595 }
1596
1597 static bool set_char_size(std::ostream& a_out,FT_Face& a_face,float a_height,float& a_scale) {
1598
1599 // arrange char_width, char_height and m_scale so
1600 // that text height be 1 in world coordinates.
1601
1602 // What these fancy 26.6, 64, 72 number mean ?
1603 // In what unit do we receive points in callbacks ?
1604 // Knowing char_height and vres can we know height of points ?
1605
1606 FT_F26Dot6 wchar = 1000*64;
1607 FT_F26Dot6 hchar = 1000*64;
1608 FT_UInt hres = 72;
1609 FT_UInt vres = 72;
1610
1611 FT_Error error = ::FT_Set_Char_Size(a_face,wchar,hchar,hres,vres);
1612 if(error) {
1613 a_out << "toolx::sg::text_freetype::set_char_size :"
1614 << " FT_Set_Char_Size : error : " << serror(error) << "."
1615 << std::endl;
1616 ::FT_Done_Face(a_face);
1617 a_face = 0;
1618 a_scale = 1;
1619 return false;
1620 }
1621 a_scale = a_height/float(wchar);
1622 return true;
1623 }
1624
1625 static bool ascent(std::ostream& a_out,FT_Face& a_face,float a_height,float& a_value) {
1626 float scale;
1627 if(!set_char_size(a_out,a_face,a_height,scale)) {a_value = 0;return false;}
1628 FT_Pos ascent = a_face->size->metrics.ascender; //FT_Pos (long)
1629 a_value = float(ascent) * scale;
1630 return true;
1631 }
1632
1633 static bool descent(std::ostream& a_out,FT_Face& a_face,float a_height,float& a_value) {
1634 float scale;
1635 if(!set_char_size(a_out,a_face,a_height,scale)) {a_value = 0;return false;}
1636 FT_Pos descent = a_face->size->metrics.descender; //FT_Pos (long) //<0
1637 a_value = -(float(descent) * scale);
1638 return true;
1639 }
1640
1641 static bool y_advance(std::ostream& a_out,FT_Face& a_face,float a_height,float& a_adv) {
1642 float scale;
1643 if(!set_char_size(a_out,a_face,a_height,scale)) {a_adv = 0;return false;}
1644 FT_Pos face_height = a_face->size->metrics.height; //FT_Pos (long)
1645 a_adv = float(face_height)*scale;
1646 return true;
1647 }
1648
1649 static bool truncate(std::ostream& a_out,FT_Face& a_face,float a_height,
1650 const std::string& a_string,float a_cut_width,std::string& a_sout) {
1651 a_sout.clear();
1652
1653 float scale;
1654 if(!set_char_size(a_out,a_face,a_height,scale)) return false;
1655
1656 float width = 0;
1657
1658 unsigned short offset;
1659 if(!encoding_offset(a_face,offset)) return false;
1660
1661 for(auto it = a_string.cbegin(); it != a_string.cend(); ++it) {
1662 FT_ULong charcode = *it + offset; //charcode is UTF-32.
1663 FT_UInt glyph_index = ::FT_Get_Char_Index(a_face,charcode);
1664 //NOTE : if not found -> glyph_index = 0 which is the "missing glyph".
1665 if((FT_Long)glyph_index>=a_face->num_glyphs) {
1666 a_sout.clear();
1667 ::FT_Done_Face(a_face);
1668 a_face = 0;
1669 return false;
1670 }
1671
1672 {FT_Error error = ::FT_Load_Glyph(a_face,glyph_index,load_flags());
1673 if(error) {
1674 a_sout.clear();
1675 ::FT_Done_Face(a_face);
1676 a_face = 0;
1677 return false;
1678 }}
1679
1680 float cwidth = float(a_face->glyph->metrics.width)*scale;
1681 float advance = float(a_face->glyph->advance.x)*scale;
1682 if((width+cwidth)>=a_cut_width) return true;
1683 a_sout += *it;
1684 width += advance;
1685 }
1686
1687 return true;
1688 }
1689
1690 static bool get_bounds(std::ostream& a_out,FT_Face& a_face,float a_height,
1691 const std::vector<std::string>& a_text,
1692 float& a_mn_x,float& a_mn_y,float& a_mn_z,
1693 float& a_mx_x,float& a_mx_y,float& a_mx_z){
1694 tools::box_3f_make_empty(a_mn_x,a_mn_y,a_mn_z,a_mx_x,a_mx_y,a_mx_z);
1695
1696 if(a_text.empty()) return true;
1697
1698 float scale;
1699 if(!set_char_size(a_out,a_face,a_height,scale)) return false;
1700
1701 float xmx = 0;
1702
1703 unsigned short offset;
1704 if(!encoding_offset(a_face,offset)) return false;
1705
1706 tools_vforcit(std::string,a_text,vit) {
1707 const std::string& line = *vit;
1708
1709 float width = 0;
1710 for(auto it = line.cbegin(); it != line.cend(); ++it) {
1711 FT_ULong charcode = *it + offset; //charcode is UTF-32.
1712 FT_UInt glyph_index = ::FT_Get_Char_Index(a_face,charcode);
1713 //NOTE : if not found -> glyph_index = 0 which is the "missing glyph".
1714 if((FT_Long)glyph_index>=a_face->num_glyphs) {
1715 ::FT_Done_Face(a_face);
1716 a_face = 0;
1717 return false;
1718 }
1719
1720 {FT_Error error = ::FT_Load_Glyph(a_face,glyph_index,load_flags());
1721 if(error) {
1722 ::FT_Done_Face(a_face);
1723 a_face = 0;
1724 return false;
1725 }}
1726
1727 float advance = float(a_face->glyph->advance.x)*scale;
1728 width += advance;
1729 }
1730
1731 xmx = tools::max_of<float>(xmx,width);
1732 }
1733
1734 FT_Pos ascent = a_face->size->metrics.ascender; //FT_Pos (long)
1735 FT_Pos descent = a_face->size->metrics.descender; //FT_Pos (long) //<0
1736 FT_Pos face_height = a_face->size->metrics.height; //FT_Pos (long)
1737
1738 float ymn = -float(face_height)*scale*(a_text.size()-1) //height>0
1739 +float(descent)*scale;
1740
1741 a_mn_x = 0;
1742 a_mn_y = ymn;
1743 a_mn_z = 0;
1744 a_mx_x = xmx;
1745 a_mx_y = float(ascent)*scale;
1746 a_mx_z = 0;
1747
1748 return true;
1749 }
1750
1751 static bool get_bounds(std::ostream& a_out,FT_Face& a_face,float a_height,
1752 const std::vector<uniline>& a_text,
1753 float& a_mn_x,float& a_mn_y,float& a_mn_z,
1754 float& a_mx_x,float& a_mx_y,float& a_mx_z){
1755 tools::box_3f_make_empty(a_mn_x,a_mn_y,a_mn_z,a_mx_x,a_mx_y,a_mx_z);
1756
1757 if(a_text.empty()) return true;
1758
1759 float scale;
1760 if(!set_char_size(a_out,a_face,a_height,scale)) return false;
1761
1762 float xmx = 0;
1763
1764 tools_vforcit(uniline,a_text,vit) {
1765 const uniline& line = *vit;
1766 float width = 0;
1767
1768 tools_vforcit(unichar,line,it) {
1769
1770 FT_ULong charcode = *it; //charcode is UTF-32.
1771 FT_UInt glyph_index = ::FT_Get_Char_Index(a_face,charcode);
1772 //NOTE : if not found -> glyph_index = 0 which is the "missing glyph".
1773 if((FT_Long)glyph_index>=a_face->num_glyphs) {
1774 ::FT_Done_Face(a_face);
1775 a_face = 0;
1776 return false;
1777 }
1778
1779 {FT_Error error = ::FT_Load_Glyph(a_face,glyph_index,load_flags());
1780 if(error) {
1781 ::FT_Done_Face(a_face);
1782 a_face = 0;
1783 return false;
1784 }}
1785
1786 float advance = float(a_face->glyph->advance.x)*scale;
1787 width += advance;
1788 }
1789
1790 xmx = tools::max_of<float>(xmx,width);
1791 }
1792
1793 FT_Pos ascent = a_face->size->metrics.ascender; //FT_Pos (long)
1794 FT_Pos descent = a_face->size->metrics.descender; //FT_Pos (long) //<0
1795 FT_Pos face_height = a_face->size->metrics.height; //FT_Pos (long)
1796
1797 float ymn = -float(face_height)*scale*(a_text.size()-1) //height>0
1798 +float(descent)*scale;
1799
1800 a_mn_x = 0;
1801 a_mn_y = ymn;
1802 a_mn_z = 0;
1803 a_mx_x = xmx;
1804 a_mx_y = float(ascent)*scale;
1805 a_mx_z = 0;
1806
1807 return true;
1808 }
1809
1810 static bool encoding_offset(FT_Face& a_face,unsigned short& a_offset) {
1811
1812 // arialbd.ttf :
1813 // num charmap 2
1814 // charmap 0, platform 1, encoding 0.
1815 // charmap 1, platform 3, encoding 1.
1816
1817 // symbol.ttf :
1818 // num charmap 2
1819 // charmap 0, platform 1, encoding 0.
1820 // charmap 1, platform 3, encoding 0.
1821
1822 // stixgeneral.otf :
1823 // num charmap 6.
1824 // charmap 0, platform 0, encoding 3.
1825 // charmap 1, platform 0, encoding 4.
1826 // charmap 2, platform 1, encoding 0.
1827 // charmap 3, platform 3, encoding 1.
1828
1829 a_offset = 0;
1830
1831 // cooking to handle symbol.ttf and wingding.ttf :
1832 FT_Int n = a_face->num_charmaps;
1833 FT_Int i;
1834 for ( i = 0; i < n; i++ ) {
1835 FT_CharMap charmap = a_face->charmaps[i];
1836 unsigned short platform = charmap->platform_id;
1837 unsigned short encoding = charmap->encoding_id;
1838
1839 if ( (platform == 3 && encoding == 1 ) ||
1840 (platform == 3 && encoding == 0 ) ||
1841 (platform == 0 && encoding == 0 ) ) {
1842 FT_Error error = FT_Set_Charmap(a_face,charmap);
1843 if(error) {
1844 ::FT_Done_Face(a_face);
1845 a_face = 0;
1846 a_offset = 0;
1847 return false;
1848 }
1849 // For symbol.ttf and wingding.ttf
1850 if (platform == 3 && encoding == 0 ) a_offset = 0xF000;
1851 return true;
1852
1853 } else {
1854 }
1855 }
1856
1857 ::FT_Done_Face(a_face);
1858 a_face = 0;
1859 a_offset = 0;
1860 return false;
1861 }
1862
1863 protected:
1864 FT_Library m_library;
1865 FT_Face m_face;
1866 unsigned short m_encoding_offset;
1867 bool m_verbose; //append _ to avoid clash with tools/sg/guib::m_verbose
1868 ////////////////////////////////////////////////////////
1869 /// outline ////////////////////////////////////////////
1870 ////////////////////////////////////////////////////////
1871 FT_Outline_Funcs m_funcs; //See doc in ftimage.h
1872 float m_last_x,m_last_y;
1873 float m_scale;
1874 float m_trans_x,m_trans_y;
1875 size_t m_steps;
1876 float m_delta;
1877 float m_delta2;
1878 float m_delta3;
1879
1880 std::vector<float> m_xys;
1881 ////////////////////////////////////////////////////////
1882 /// outline lines //////////////////////////////////////
1883 ////////////////////////////////////////////////////////
1884 typedef std::pair<size_t,size_t> line_t; //pos in m_xys.
1885 typedef std::vector<line_t> lines_t;
1886 lines_t m_lines;
1887 ////////////////////////////////////////////////////////
1888 /// outline triangles //////////////////////////////////
1889 ////////////////////////////////////////////////////////
1890 GLUtesselator* m_tobj;
1891 bool m_contour_open;
1892 std::vector<double*> m_glutess_trids;
1893 size_t m_glutess_trids_num;
1894 std::vector<double*> m_combine_trids;
1895 size_t m_combine_trids_num;
1896 GLUenum m_mode;
1897 typedef std::pair<size_t,size_t> triangle_t; //pos in m_xys.
1898 typedef std::pair<GLUenum,triangle_t> gl_triangle_t;
1899 typedef std::vector<gl_triangle_t> triangles_t;
1900 triangles_t m_triangles;
1901 size_t m_pos;
1902 size_t m_gsto_lines_sz;
1903 size_t m_gsto_sz;
1904 wndg_type m_wndg;
1905 tools::colorf m_front_color;
1906 tools::sg::group m_bitmaps;
1907 std::vector<tools::sg::tex_quadrilateral*> m_tqs;
1908 float m_char_height;
1909 };
1910
1911 }}
1912
1913
1914 #endif