1 /*! ========================================================================
4 ** $Id: lyr_freetype.cpp,v 1.5 2005/01/24 05:00:18 darco Exp $
6 ** Copyright (c) 2002 Robert B. Quattlebaum Jr.
8 ** This software and associated documentation
9 ** are CONFIDENTIAL and PROPRIETARY property of
10 ** the above-mentioned copyright holder.
12 ** You may not copy, print, publish, or in any
13 ** other way distribute this software without
14 ** a prior written agreement with
15 ** the copyright holder.
17 ** === N O T E S ===========================================================
19 ** ========================================================================= */
21 /* === H E A D E R S ======================================================= */
32 #include "lyr_freetype.h"
34 #include <sinfg/string.h>
35 #include <sinfg/time.h>
36 #include <sinfg/context.h>
37 #include <sinfg/paramdesc.h>
38 #include <sinfg/renddesc.h>
39 #include <sinfg/surface.h>
40 #include <sinfg/value.h>
41 #include <sinfg/valuenode.h>
42 #include <sinfg/canvas.h>
50 using namespace sinfg;
52 /* === M A C R O S ========================================================= */
54 #define MAX_GLYPHS 2000
56 #define PANGO_STYLE_NORMAL (0)
57 #define PANGO_STYLE_OBLIQUE (1)
58 #define PANGO_STYLE_ITALIC (2)
61 #define WEIGHT_NORMAL (400)
62 #define WEIGHT_BOLD (700)
64 /* === G L O B A L S ======================================================= */
66 SINFG_LAYER_INIT(lyr_freetype);
67 SINFG_LAYER_SET_NAME(lyr_freetype,"text");
68 SINFG_LAYER_SET_LOCAL_NAME(lyr_freetype,_("Simple Text"));
69 SINFG_LAYER_SET_CATEGORY(lyr_freetype,_("Typography"));
70 SINFG_LAYER_SET_VERSION(lyr_freetype,"0.2");
71 SINFG_LAYER_SET_CVS_ID(lyr_freetype,"$Id: lyr_freetype.cpp,v 1.5 2005/01/24 05:00:18 darco Exp $");
73 /* === P R O C E D U R E S ================================================= */
77 if(glyph)FT_Done_Glyph(glyph);
81 TextLine::clear_and_free()
83 std::vector<Glyph>::iterator iter;
84 for(iter=glyph_table.begin();iter!=glyph_table.end();++iter)
86 if(iter->glyph)FT_Done_Glyph(iter->glyph);
92 /* === M E T H O D S ======================================================= */
94 lyr_freetype::lyr_freetype()
98 size=Vector(0.25,0.25);
100 color=Color::black();
102 orient=Vector(0.5,0.5);
105 weight=WEIGHT_NORMAL;
106 style=PANGO_STYLE_NORMAL;
111 set_blend_method(Color::BLEND_COMPOSITE);
114 new_font(family,style,weight);
119 lyr_freetype::~lyr_freetype()
126 lyr_freetype::new_font(const sinfg::String &family, int style, int weight)
129 !new_font_(family,style,weight) &&
130 !new_font_(family,style,WEIGHT_NORMAL) &&
131 !new_font_(family,PANGO_STYLE_NORMAL,weight) &&
132 !new_font_(family,PANGO_STYLE_NORMAL,WEIGHT_NORMAL) &&
133 !new_font_("sans serif",style,weight) &&
134 !new_font_("sans serif",style,WEIGHT_NORMAL) &&
135 !new_font_("sans serif",PANGO_STYLE_NORMAL,weight)
137 new_font_("sans serif",PANGO_STYLE_NORMAL,WEIGHT_NORMAL);
141 lyr_freetype::new_font_(const sinfg::String &family_, int style, int weight)
143 sinfg::String family(family_);
146 for(int i=0;i<family.size();i++)family[i]=tolower(family[i]);
149 if(family=="arial black")
151 if(new_face("ariblk"))
157 if(family=="sans serif" || family=="arial")
159 String arial("arial");
160 if(weight>WEIGHT_NORMAL)
162 if(style==PANGO_STYLE_ITALIC||style==PANGO_STYLE_OBLIQUE)
165 if(weight>WEIGHT_NORMAL) arial+='d';
171 if(family=="comic" || family=="comic sans")
173 String filename("comic");
174 if(weight>WEIGHT_NORMAL)
176 if(style==PANGO_STYLE_ITALIC||style==PANGO_STYLE_OBLIQUE)
178 else if(weight>WEIGHT_NORMAL) filename+='d';
180 if(new_face(filename))
184 if(family=="courier" || family=="courier new")
186 String filename("cour");
187 if(weight>WEIGHT_NORMAL)
189 if(style==PANGO_STYLE_ITALIC||style==PANGO_STYLE_OBLIQUE)
191 else if(weight>WEIGHT_NORMAL) filename+='d';
193 if(new_face(filename))
197 if(family=="serif" || family=="times" || family=="times new roman")
199 String filename("times");
200 if(weight>WEIGHT_NORMAL)
202 if(style==PANGO_STYLE_ITALIC||style==PANGO_STYLE_OBLIQUE)
204 else if(weight>WEIGHT_NORMAL) filename+='d';
206 if(new_face(filename))
210 if(family=="trebuchet")
212 String filename("trebuc");
213 if(weight>WEIGHT_NORMAL)
215 if(style==PANGO_STYLE_ITALIC||style==PANGO_STYLE_OBLIQUE)
218 if(weight<=WEIGHT_NORMAL) filename+='t';
220 else if(weight>WEIGHT_NORMAL) filename+='d';
222 if(new_face(filename))
227 if(family=="sans serif" || family=="luxi sans")
230 String luxi("luxis");
231 if(weight>WEIGHT_NORMAL)
235 if(style==PANGO_STYLE_ITALIC||style==PANGO_STYLE_OBLIQUE)
242 if(new_face("arial"))
244 if(new_face("Arial"))
247 if(family=="serif" || family=="times" || family=="times new roman" || family=="luxi serif")
250 String luxi("luxir");
251 if(weight>WEIGHT_NORMAL)
255 if(style==PANGO_STYLE_ITALIC||style==PANGO_STYLE_OBLIQUE)
261 if(new_face("Times New Roman"))
263 if(new_face("Times"))
269 String luxi("luxim");
270 if(weight>WEIGHT_NORMAL)
274 if(style==PANGO_STYLE_ITALIC||style==PANGO_STYLE_OBLIQUE)
281 if(new_face("Times New Roman"))
283 if(new_face("Times"))
287 return new_face(family_) || new_face(family);
293 lyr_freetype::new_face(const String &newfont)
298 // If we are already loaded, don't bother reloading.
299 if(face && font==newfont)
308 error=FT_New_Face(ft_library,newfont.c_str(),face_index,&face);
309 if(error)error=FT_New_Face(ft_library,(newfont+".ttf").c_str(),face_index,&face);
313 if(error)error=FT_New_Face(ft_library,(get_canvas()->get_file_path()+ETL_DIRECTORY_SEPERATOR+newfont).c_str(),face_index,&face);
314 if(error)error=FT_New_Face(ft_library,(get_canvas()->get_file_path()+ETL_DIRECTORY_SEPERATOR+newfont+".ttf").c_str(),face_index,&face);
318 if(error)error=FT_New_Face(ft_library,("C:\\WINDOWS\\FONTS\\"+newfont).c_str(),face_index,&face);
319 if(error)error=FT_New_Face(ft_library,("C:\\WINDOWS\\FONTS\\"+newfont+".ttf").c_str(),face_index,&face);
321 if(error)error=FT_New_Face(ft_library,("/usr/share/fonts/truetype/"+newfont).c_str(),face_index,&face);
322 if(error)error=FT_New_Face(ft_library,("/usr/share/fonts/truetype/"+newfont+".ttf").c_str(),face_index,&face);
324 if(error)error=FT_New_Face(ft_library,("/usr/X11R6/lib/X11/fonts/TTF/"+newfont).c_str(),face_index,&face);
325 if(error)error=FT_New_Face(ft_library,("/usr/X11R6/lib/X11/fonts/TTF/"+newfont+".ttf").c_str(),face_index,&face);
327 if(error)error=FT_New_Face(ft_library,("/usr/X11R6/lib/X11/fonts/truetype/"+newfont).c_str(),face_index,&face);
328 if(error)error=FT_New_Face(ft_library,("/usr/X11R6/lib/X11/fonts/truetype/"+newfont+".ttf").c_str(),face_index,&face);
330 if(error)error=FT_New_Face(ft_library,("/usr/X11R6/lib/X11/fonts/type1/"+newfont).c_str(),face_index,&face);
331 if(error)error=FT_New_Face(ft_library,("/usr/X11R6/lib/X11/fonts/type1/"+newfont+".ttf").c_str(),face_index,&face);
334 if(error)error=FT_New_Face(ft_library,("~/Library/Fonts/"+newfont).c_str(),face_index,&face);
335 if(error)error=FT_New_Face(ft_library,("~/Library/Fonts/"+newfont+".ttf").c_str(),face_index,&face);
337 if(error)error=FT_New_Face(ft_library,("/Library/Fonts/"+newfont).c_str(),face_index,&face);
338 if(error)error=FT_New_Face(ft_library,("/Library/Fonts/"+newfont+".ttf").c_str(),face_index,&face);
344 //sinfg::error(strprintf("lyr_freetype:%s (err=%d)",_("Unable to open face."),error));
355 lyr_freetype::set_param(const String & param, const ValueBase &value)
357 Mutex::Lock lock(mutex);
358 if(param=="font" && value.same_as(font))
360 new_font(etl::basename(value.get(font)),style,weight);
361 family=etl::basename(value.get(font));
364 IMPORT_PLUS(family,new_font(family,style,weight));
365 IMPORT_PLUS(weight,new_font(family,style,weight));
366 IMPORT_PLUS(style,new_font(family,style,weight));
367 IMPORT_PLUS(size, if(old_version){size/=2.0;} needs_sync_=true );
368 IMPORT_PLUS(text,needs_sync_=true);
369 IMPORT_PLUS(pos,needs_sync_=true);
372 IMPORT_PLUS(orient,needs_sync_=true);
373 IMPORT_PLUS(compress,needs_sync_=true);
374 IMPORT_PLUS(vcompress,needs_sync_=true);
375 IMPORT_PLUS(use_kerning,needs_sync_=true);
376 IMPORT_PLUS(grid_fit,needs_sync_=true);
378 return Layer_Composite::set_param(param,value);
382 lyr_freetype::get_param(const String& param)const
402 return Layer_Composite::get_param(param);
406 lyr_freetype::get_param_vocab(void)const
408 Layer::Vocab ret(Layer_Composite::get_param_vocab());
410 ret.push_back(ParamDesc("text")
411 .set_local_name(_("Text"))
412 .set_description(_("Text to Render"))
413 .set_hint("paragraph")
416 ret.push_back(ParamDesc("color")
417 .set_local_name(_("Color"))
418 .set_description(_("Color of the text"))
421 ret.push_back(ParamDesc("family")
422 .set_local_name(_("Font Family"))
423 .set_hint("font_family")
426 ret.push_back(ParamDesc("style")
427 .set_local_name(_("Style"))
429 .add_enum_value(PANGO_STYLE_NORMAL, "normal" ,_("Normal"))
430 .add_enum_value(PANGO_STYLE_OBLIQUE, "oblique" ,_("Oblique"))
431 .add_enum_value(PANGO_STYLE_ITALIC, "italic" ,_("Italic"))
434 ret.push_back(ParamDesc("weight")
435 .set_local_name(_("Weight"))
437 .add_enum_value(200, "ultralight" ,_("Ultralight"))
438 .add_enum_value(300, "light" ,_("light"))
439 .add_enum_value(400, "normal" ,_("Normal"))
440 .add_enum_value(700, "bold" ,_("Bold"))
441 .add_enum_value(800, "ultrabold" ,_("Ultrabold"))
442 .add_enum_value(900, "heavy" ,_("Heavy"))
444 ret.push_back(ParamDesc("compress")
445 .set_local_name(_("Hozontal Spacing"))
446 .set_description(_("Describes how close glyphs are horizontally"))
449 ret.push_back(ParamDesc("vcompress")
450 .set_local_name(_("Vertical Spacing"))
451 .set_description(_("Describes how close lines of text are vertically"))
454 ret.push_back(ParamDesc("size")
455 .set_local_name(_("Size"))
456 .set_description(_("Size of the text"))
462 ret.push_back(ParamDesc("orient")
463 .set_local_name(_("Orientation"))
464 .set_description(_("Text Orientation"))
465 .set_invisible_duck()
468 ret.push_back(ParamDesc("pos")
469 .set_local_name(_("Position"))
470 .set_description(_("Text Position"))
473 ret.push_back(ParamDesc("font")
474 .set_local_name(_("Font"))
475 .set_description(_("Filename of the font to use"))
476 .set_hint("filename")
481 ret.push_back(ParamDesc("use_kerning")
482 .set_local_name(_("Kerning"))
483 .set_description(_("Enables/Disables font kerning (If the font supports it)"))
486 ret.push_back(ParamDesc("grid_fit")
487 .set_local_name(_("Sharpen Edges"))
488 .set_description(_("Turn this off if you are going to be animating the text"))
490 ret.push_back(ParamDesc("invert")
491 .set_local_name(_("Invert"))
507 lyr_freetype::get_color(Context context, const Point &pos)const
510 const_cast<lyr_freetype*>(this)->sync();
513 return context.get_color(pos);
514 return context.get_color(pos);
518 lyr_freetype::accelerated_render(Context context,Surface *surface,int quality, const RendDesc &renddesc, ProgressCallback *cb)const
520 static sinfg::RecMutex freetype_mutex;
523 const_cast<lyr_freetype*>(this)->sync();
529 Vector size(lyr_freetype::size*2);
531 if(!context.accelerated_render(surface,quality,renddesc,cb))
534 if(is_disabled() || text.empty())
537 // If there is no font loaded, just bail
540 if(cb)cb->warning(string("lyr_freetype:")+_("No face loaded, no text will be rendered."));
544 String text(lyr_freetype::text);
545 if(text=="@_FILENAME_@" && get_canvas() && !get_canvas()->get_file_name().empty())
547 text=basename(get_canvas()->get_file_name());
550 // Width and Height of a pixel
551 Vector::value_type pw=renddesc.get_w()/(renddesc.get_br()[0]-renddesc.get_tl()[0]);
552 Vector::value_type ph=renddesc.get_h()/(renddesc.get_br()[1]-renddesc.get_tl()[1]);
554 // Calculate character width and height
555 int w=abs(round_to_int(size[0]*pw));
556 int h=abs(round_to_int(size[1]*ph));
558 //int bx=(int)((pos[0]-renddesc.get_tl()[0])*pw*64+0.5);
559 //int by=(int)((pos[1]-renddesc.get_tl()[1])*ph*64+0.5);
563 // If the font is the size of a pixel, don't bother rendering any text
566 if(cb)cb->warning(string("lyr_freetype:")+_("Text too small, no text will be rendered."));
570 sinfg::RecMutex::Lock lock(freetype_mutex);
572 #define CHAR_RESOLUTION (64)
573 error = FT_Set_Char_Size(
574 face, // handle to face object
575 (int)CHAR_RESOLUTION, // char_width in 1/64th of points
576 (int)CHAR_RESOLUTION, // char_height in 1/64th of points
577 round_to_int(abs(size[0]*pw*CHAR_RESOLUTION)), // horizontal device resolution
578 round_to_int(abs(size[1]*ph*CHAR_RESOLUTION)) ); // vertical device resolution
580 // Here is where we can compensate for the
581 // error in freetype's rendering engine.
582 const float xerror(abs(size[0]*pw)/(float)face->size->metrics.x_ppem/1.13f/0.996);
583 const float yerror(abs(size[1]*ph)/(float)face->size->metrics.y_ppem/1.13f/0.996);
584 //sinfg::info("xerror=%f, yerror=%f",xerror,yerror);
585 const float compress(lyr_freetype::compress*xerror);
586 const float vcompress(lyr_freetype::vcompress*yerror);
590 if(cb)cb->error(string("lyr_freetype:")+_("Unable to set face size.")+strprintf(" (err=%d)",error));
594 FT_GlyphSlot slot = face->glyph; // a small shortcut
595 FT_UInt glyph_index(0);
599 std::list<TextLine> lines;
602 -- ** -- CREATE GLYPHS -------------------------------------------------------
605 lines.push_front(TextLine());
606 string::const_iterator iter;
607 for (iter=text.begin(); iter!=text.end(); ++iter)
612 lines.push_front(TextLine());
621 glyph_index = FT_Get_Char_Index( face, ' ' );
624 glyph_index = FT_Get_Char_Index( face, *iter );
626 // retrieve kerning distance and move pen position
627 if ( FT_HAS_KERNING(face) && use_kerning && previous && glyph_index )
632 FT_Get_Kerning( face, previous, glyph_index, ft_kerning_default, &delta );
634 FT_Get_Kerning( face, previous, glyph_index, ft_kerning_unfitted, &delta );
638 bx += round_to_int(delta.x*compress);
639 by += round_to_int(delta.y*compress);
650 // store current pen position
651 curr_glyph.pos.x = bx;
652 curr_glyph.pos.y = by;
654 // load glyph image into the slot. DO NOT RENDER IT !!
656 error = FT_Load_Glyph( face, glyph_index, FT_LOAD_DEFAULT);
658 error = FT_Load_Glyph( face, glyph_index, FT_LOAD_DEFAULT|FT_LOAD_NO_HINTING );
659 if (error) continue; // ignore errors, jump to next glyph
661 // extract glyph image and store it in our table
662 error = FT_Get_Glyph( face->glyph, &curr_glyph.glyph );
663 if (error) continue; // ignore errors, jump to next glyph
665 // record current glyph index
666 previous = glyph_index;
668 // Update the line width
669 lines.front().width=bx+slot->advance.x;
671 // increment pen position
673 bx += round_to_int(slot->advance.x*multiplier*compress)-bx%round_to_int(slot->advance.x*multiplier*compress);
675 bx += round_to_int(slot->advance.x*compress*multiplier);
677 //bx += round_to_int(slot->advance.x*compress*multiplier);
678 //by += round_to_int(slot->advance.y*compress);
679 by += slot->advance.y*multiplier;
681 lines.front().glyph_table.push_back(curr_glyph);
686 //float string_height;
687 //string_height=(((lines.size()-1)*face->size->metrics.height+lines.back().actual_height()));
689 //int string_height=face->size->metrics.ascender;
690 //#define METRICS_SCALE_ONE (65536.0f)
691 #define METRICS_SCALE_ONE ((float)(1<<16))
694 line_height=vcompress*((float)face->height*(((float)face->size->metrics.y_scale/METRICS_SCALE_ONE)));
697 string_height=round_to_int(((lines.size()-1)*line_height+lines.back().actual_height()));
698 //sinfg::info("string_height=%d",string_height);
699 //sinfg::info("line_height=%f",line_height);
702 -- ** -- RENDER THE GLYPHS ---------------------------------------------------
706 Surface *src_surface;
713 Surface::alpha_pen pen(surface->begin(),get_amount(),get_blend_method());
715 surface->fill(color,pen,src_.get_w(),src_.get_h());
721 std::list<TextLine>::iterator iter;
723 for(curr_line=0,iter=lines.begin();iter!=lines.end();++iter,curr_line++)
725 bx=round_to_int((pos[0]-renddesc.get_tl()[0])*pw*CHAR_RESOLUTION-orient[0]*iter->width);
726 by=round_to_int((pos[1]-renddesc.get_tl()[1])*ph*CHAR_RESOLUTION+(1.0-orient[1])*string_height-line_height*curr_line);
727 //by=round_to_int(vcompress*((pos[1]-renddesc.get_tl()[1])*ph*64+(1.0-orient[1])*string_height-face->size->metrics.height*curr_line));
728 //sinfg::info("curr_line=%d, bx=%d, by=%d",curr_line,bx,by);
730 std::vector<Glyph>::iterator iter2;
731 for(iter2=iter->glyph_table.begin();iter2!=iter->glyph_table.end();++iter2)
733 FT_Glyph image(iter2->glyph);
737 pen.x = bx + iter2->pos.x;
738 pen.y = by + iter2->pos.y;
740 //sinfg::info("GLYPH: pen.x=%d, pen,y=%d",curr_line,(pen.x+32)>>6,(pen.y+32)>>6);
742 error = FT_Glyph_To_Bitmap( &image, ft_render_mode_normal,0/*&pen*/, 1 );
743 if(error) { FT_Done_Glyph( image ); continue; }
745 bit = (FT_BitmapGlyph)image;
747 for(v=0;v<bit->bitmap.rows;v++)
748 for(u=0;u<bit->bitmap.width;u++)
750 int x=u+((pen.x+32)>>6)+ bit->left;
751 int y=v+((pen.y+32)>>6)- bit->top;
754 y<surface->get_h() &&
757 float myamount=(float)bit->bitmap.buffer[v*bit->bitmap.pitch+u]/255.0f;
759 myamount=1.0f-myamount;
760 (*surface)[y][x]=Color::blend(color,(*src_surface)[y][x],myamount*get_amount(),get_blend_method());
764 FT_Done_Glyph( image );
766 //iter->clear_and_free();
775 lyr_freetype::get_bounding_rect()const
778 const_cast<lyr_freetype*>(this)->sync();
779 // if(!is_disabled())
780 return Rect::full_plane();