我要发帖 回复

管理员

735

主题

2万

积分

30

专家分

忠于职守杰出贡献鼓励

兴趣点(最多三项):

建模技术

私信
发表时间 : 2009-8-17 13:09:21 | 浏览 : 2968    评论 : 0
作者:mybios
前言:
Freetype是一个跨平台、开源的字体渲染器,网上很多文章介绍,本人就不啰嗦了。本文重点在于实现文章标题所属的各种效果,不是Freetype的基本使用方法介绍文档,所以对于Freetype不熟悉的同学们请先学习下Freetype的基本用法,才可以使用本文中所提及的方法。

正文:
用FreeType实现矢量字体的粗体、斜体、描边、阴影效果不是一件容易的事,本人认为皆因Freetype的接口太过于底层化,Freetype没有对其进行上层包装,所以要实现这些对于软件/游戏来说的基本效果,都是件挺麻烦的事情。不过,问题总是会有解决方法的,这些效果的实现,请听本人一个个道来:

1.      加粗
加粗可以使用FreeType中的一个API来实现FT_Outline_Embolden,但是这个API不支持水平垂直方向加粗量的分别设置,所以,需要参照FT_Outline_Embolden的实现重新编写一个函数,GDI++已经做了这个事情,引用它的代码:


  1. // 就是FT_Outline_Embolden
  2. FT_Error Old_FT_Outline_Embolden( FT_Outline*  outline, FT_Pos strength )
  3. {
  4.     FT_Vector*    points;
  5.     FT_Vector    v_prev, v_first, v_next, v_cur;
  6.     FT_Angle    rotate, angle_in, angle_out;
  7.     FT_Int        c, n, first;
  8.     FT_Int        orientation;

  9.     if ( !outline )
  10.         return FT_Err_Invalid_Argument;

  11.     strength /= 2;
  12.     if ( strength == 0 )
  13.         return FT_Err_Ok;

  14.     orientation = FT_Outline_Get_Orientation( outline );
  15.     if ( orientation == FT_ORIENTATION_NONE )
  16.     {
  17.         if ( outline->n_contours )
  18.             return FT_Err_Invalid_Argument;
  19.         else
  20.             return FT_Err_Ok;
  21.     }

  22.     if ( orientation == FT_ORIENTATION_TRUETYPE )
  23.         rotate = -FT_ANGLE_PI2;
  24.     else
  25.         rotate = FT_ANGLE_PI2;

  26.     points = outline->points;

  27.     first = 0;
  28.     for ( c = 0; c < outline->n_contours; c++ )
  29.     {
  30.         int  last = outline->contours[c];

  31.         v_first = points[first];
  32.         v_prev  = points[last];
  33.         v_cur   = v_first;

  34.         for ( n = first; n <= last; n++ )
  35.         {
  36.             FT_Vector    in, out;
  37.             FT_Angle    angle_diff;
  38.             FT_Pos        d;
  39.             FT_Fixed    scale;

  40.             if ( n < last )
  41.                 v_next = points[n + 1];
  42.             else
  43.                 v_next = v_first;

  44.             /**//* compute the in and out vectors */
  45.             in.x = v_cur.x - v_prev.x;
  46.             in.y = v_cur.y - v_prev.y;

  47.             out.x = v_next.x - v_cur.x;
  48.             out.y = v_next.y - v_cur.y;

  49.             angle_in   = FT_Atan2( in.x, in.y );
  50.             angle_out  = FT_Atan2( out.x, out.y );
  51.             angle_diff = FT_Angle_Diff( angle_in, angle_out );
  52.             scale      = FT_Cos( angle_diff / 2 );

  53.             if ( scale < 0x4000L && scale > -0x4000L )
  54.                 in.x = in.y = 0;
  55.             else
  56.             {
  57.                 d = FT_DivFix( strength, scale );

  58.                 FT_Vector_From_Polar( &in, d, angle_in + angle_diff / 2 - rotate );
  59.             }

  60.             outline->points[n].x = v_cur.x + strength + in.x;
  61.             //伀偙傟傪僐儊儞僩傾僂僩偟偨偩偗
  62.             //outline->points[n].y = v_cur.y + strength + in.y;

  63.             v_prev = v_cur;
  64.             v_cur  = v_next;
  65.         }

  66.         first = last + 1;
  67.     }

  68.     return FT_Err_Ok;
  69. }

  70. // 垂直加粗
  71. FT_Error Vert_FT_Outline_Embolden( FT_Outline*  outline, FT_Pos strength )
  72. {
  73.     FT_Vector*    points;
  74.     FT_Vector    v_prev, v_first, v_next, v_cur;
  75.     FT_Angle    rotate, angle_in, angle_out;
  76.     FT_Int        c, n, first;
  77.     FT_Int        orientation;

  78.     if ( !outline )
  79.         return FT_Err_Invalid_Argument;

  80.     strength /= 2;
  81.     if ( strength == 0 )
  82.         return FT_Err_Ok;

  83.     orientation = FT_Outline_Get_Orientation( outline );
  84.     if ( orientation == FT_ORIENTATION_NONE )
  85.     {
  86.         if ( outline->n_contours )
  87.             return FT_Err_Invalid_Argument;
  88.         else
  89.             return FT_Err_Ok;
  90.     }

  91.     if ( orientation == FT_ORIENTATION_TRUETYPE )
  92.         rotate = -FT_ANGLE_PI2;
  93.     else
  94.         rotate = FT_ANGLE_PI2;

  95.     points = outline->points;

  96.     first = 0;
  97.     for ( c = 0; c < outline->n_contours; c++ )
  98.     {
  99.         int  last = outline->contours[c];

  100.         v_first = points[first];
  101.         v_prev  = points[last];
  102.         v_cur   = v_first;

  103.         for ( n = first; n <= last; n++ )
  104.         {
  105.             FT_Vector    in, out;
  106.             FT_Angle    angle_diff;
  107.             FT_Pos        d;
  108.             FT_Fixed    scale;

  109.             if ( n < last )
  110.                 v_next = points[n + 1];
  111.             else
  112.                 v_next = v_first;

  113.             /**//* compute the in and out vectors */
  114.             in.x = v_cur.x - v_prev.x;
  115.             in.y = v_cur.y - v_prev.y;

  116.             out.x = v_next.x - v_cur.x;
  117.             out.y = v_next.y - v_cur.y;

  118.             angle_in   = FT_Atan2( in.x, in.y );
  119.             angle_out  = FT_Atan2( out.x, out.y );
  120.             angle_diff = FT_Angle_Diff( angle_in, angle_out );
  121.             scale      = FT_Cos( angle_diff / 2 );

  122.             if ( scale < 0x4000L && scale > -0x4000L )
  123.                 in.x = in.y = 0;
  124.             else
  125.             {
  126.                 d = FT_DivFix( strength, scale );

  127.                 FT_Vector_From_Polar( &in, d, angle_in + angle_diff / 2 - rotate );
  128.             }

  129.             //outline->points[n].x = v_cur.x + strength + in.x;
  130.             //仾偙傟傪僐儊儞僩傾僂僩偟偨偩偗
  131.             outline->points[n].y = v_cur.y + strength + in.y;

  132.             v_prev = v_cur;
  133.             v_cur  = v_next;
  134.         }

  135.         first = last + 1;
  136.     }

  137.     return FT_Err_Ok;
  138. }

  139. // 新的加粗函数
  140. FT_Error New_FT_Outline_Embolden( FT_Outline*  outline, FT_Pos str_h, FT_Pos str_v )
  141. {
  142.     if ( !outline ) return FT_Err_Invalid_Argument;
  143.     int orientation = FT_Outline_Get_Orientation( outline );
  144.     if ( orientation == FT_ORIENTATION_NONE )
  145.         if ( outline->n_contours ) return FT_Err_Invalid_Argument;
  146.     Vert_FT_Outline_Embolden( outline, str_v );
  147.     Old_FT_Outline_Embolden( outline, str_h );
  148.     return FT_Err_Ok;
  149. }

  150. // 让一个字体槽加粗,并且填充其他的大小属性
  151. void New_GlyphSlot_Embolden( FT_GlyphSlot  slot , const Vector2Float &embolden)
  152. {
  153.     if(embolden == Vector2Float::ZERO)
  154.         return;
  155.     FT_Library  library = slot->library;
  156.     FT_Face     face    = slot->face;
  157.     FT_Error    error;
  158.     FT_Pos      xstr = embolden.x, ystr = embolden.y;


  159.     if ( slot->format != FT_GLYPH_FORMAT_OUTLINE &&
  160.         slot->format != FT_GLYPH_FORMAT_BITMAP )
  161.         return;

  162.     if ( slot->format == FT_GLYPH_FORMAT_OUTLINE )
  163.     {
  164.         FT_BBox oldBox;
  165.         FT_Outline_Get_CBox(&slot->outline , &oldBox);
  166.         error = New_FT_Outline_Embolden( &slot->outline, xstr , ystr);
  167.         if ( error )
  168.             return;

  169.         FT_BBox newBox;
  170.         FT_Outline_Get_CBox(&slot->outline , &newBox);
  171.         xstr = (newBox.xMax - newBox.xMin) - (oldBox.xMax - oldBox.xMin);
  172.         ystr = (newBox.yMax - newBox.yMin) - (oldBox.yMax - oldBox.yMin);
  173.     }
  174.     else if ( slot->format == FT_GLYPH_FORMAT_BITMAP )
  175.     {
  176.         xstr = FT_PIX_FLOOR( xstr );
  177.         if ( xstr == 0 )
  178.             xstr = 1 << 6;
  179.         ystr = FT_PIX_FLOOR( ystr );

  180.         error = FT_Bitmap_Embolden( library, &slot->bitmap, xstr, ystr );
  181.         if ( error )
  182.             return;
  183.     }

  184.     if ( slot->advance.x )
  185.         slot->advance.x += xstr;

  186.     if ( slot->advance.y )
  187.         slot->advance.y += ystr;

  188.     slot->metrics.width        += xstr;
  189.     slot->metrics.height       += ystr;
  190.     slot->metrics.horiBearingY += ystr;
  191.     slot->metrics.horiAdvance  += xstr;
  192.     slot->metrics.vertBearingX -= xstr / 2;
  193.     slot->metrics.vertBearingY += ystr;
  194.     slot->metrics.vertAdvance  += ystr;

  195.     if ( slot->format == FT_GLYPH_FORMAT_BITMAP )
  196.         slot->bitmap_top += ystr >> 6;
  197. }
  198. 2.      斜体
  199. 斜体在FreeType中可以通过矩阵变换来实现,只要把矩阵设置成一个切边矩阵就可以了,方法如下:

  200. // 倾斜度,越大就越斜
  201. float lean = 0.5f;
  202. FT_Matrix matrix;
  203. matrix.xx = 0x10000L;
  204. matrix.xy = lean * 0x10000L;
  205. matrix.yx = 0;
  206. matrix.yy = 0x10000L;
  207. FT_Set_Transform( face, &matrix, 0 );
复制代码
3.      描边

网上有不少文章说描边其实很简单,就是上下左右各移动一个像素渲染一次,最后在中间再渲染一次就可以了。但是,这种方法只对于位图字体有效,对于矢量字体,效果就不好了,特别是大字体,1个像素只是很细的边界而已,对于很小的字体,1像素又显得太大。

这里提供另一种实现方案,使用的是Freetype的API:

4.         使用FT_Stroker_New创建一个笔触

5.         FT_Stroker_Set设置笔触为描边

6.         把Load后的glyph通过FT_Glyph_Copy拷贝一份出来

7.         对这个拷贝出来的glyph使用FT_Glyph_StrokeBorder设置成描边渲染

8.         使用FT_Outline_Render渲染这个描边的glyph,渲染前要设置FT_Raster_Params参数成:

FT_Raster_Params params;
memset(&params, 0, sizeof(params));
params.flags = FT_RASTER_FLAG_AA | FT_RASTER_FLAG_DIRECT;
params.gray_spans = RasterCallback;


9.         在回调函数RasterCallback中实现像素化到位图中

10.     对原来的glyph执行8操作,在回调函数RasterCallback中实现像素化到位图中,像素化过程使用alphablend的方式绘制上去

11.     把位图渲染到屏幕上或保存到文件中



这个方法是Freetype的一个example,只是很少有人注意而已,源码在这里http://www.freetype.org/freetype2/docs/tutorial/example2.cpp



12. 阴影

阴影的实现就比较简单了,只要一个个像素偏移后多渲染几次就可以了,再次不多说。

最近VR访客

手机版|VR开发网 ( 津ICP备18009691号 ) 统计 网安备12019202000257

GMT+8, 2020-9-27 09:44 AM

返回顶部