아래 코드는 FreeType tag:VER-2-10-0의 소스코드 (ftoutln.c) . (근데 버전 달라도 거의 차이없을듯)

FT_EXPORT_DEF( FT_Error )
  FT_Outline_EmboldenXY( FT_Outline*  outline,
                         FT_Pos       xstrength,
                         FT_Pos       ystrength )
  {
    FT_Vector*      points;
    FT_Int          c, first, last;
    FT_Orientation  orientation;


    if ( !outline )
      return FT_THROW( Invalid_Outline );

    xstrength /= 2;
    ystrength /= 2;
    if ( xstrength == 0 && ystrength == 0 )
      return FT_Err_Ok;

    orientation = FT_Outline_Get_Orientation( outline );
    if ( orientation == FT_ORIENTATION_NONE )
    {
      if ( outline->n_contours )
        return FT_THROW( Invalid_Argument );
      else
        return FT_Err_Ok;
    }

    points = outline->points;

    first = 0;
    for ( c = 0; c < outline->n_contours; c++ )
    {
      FT_Vector  in, out, anchor, shift;
      FT_Fixed   l_in, l_out, l_anchor = 0, l, q, d;
      FT_Int     i, j, k;


      l_in = 0;
      last = outline->contours[c];

      /* pacify compiler */
      in.x = in.y = anchor.x = anchor.y = 0;

      /* Counter j cycles though the points; counter i advances only  */
      /* when points are moved; anchor k marks the first moved point. */
      for ( i = last, j = first, k = -1;
            j != i && i != k;
            j = j < last ? j + 1 : first )
      {
        if ( j != k )
        {
          out.x = points[j].x - points[i].x;
          out.y = points[j].y - points[i].y;
          l_out = (FT_Fixed)FT_Vector_NormLen( &out );

          if ( l_out == 0 )
            continue;
        }
        else
        {
          out   = anchor;
          l_out = l_anchor;
        }

        if ( l_in != 0 )
        {
          if ( k < 0 )
          {
            k        = i;
            anchor   = in;
            l_anchor = l_in;
          }

          d = FT_MulFix( in.x, out.x ) + FT_MulFix( in.y, out.y );

          /* shift only if turn is less than ~160 degrees */
          if ( d > -0xF000L )
          {
            d = d + 0x10000L;

            /* shift components along lateral bisector in proper orientation */
            shift.x = in.y + out.y;
            shift.y = in.x + out.x;
            
            if ( orientation == FT_ORIENTATION_TRUETYPE )
              shift.x = -shift.x;
            else
              shift.y = -shift.y;

            /* restrict shift magnitude to better handle collapsing segments */
            q = FT_MulFix( out.x, in.y ) - FT_MulFix( out.y, in.x );
            if ( orientation == FT_ORIENTATION_TRUETYPE )
              q = -q;

            l = FT_MIN( l_in, l_out );

            /* non-strict inequalities avoid divide-by-zero when q == l == 0 */
            if ( FT_MulFix( xstrength, q ) <= FT_MulFix( l, d ) )
              shift.x = FT_MulDiv( shift.x, xstrength, d );
            else
              shift.x = FT_MulDiv( shift.x, l, q );
            in   = out;
        l_in = l_out;
      }

      first = last + 1;
    }

    return FT_Err_Ok;
  }

 

 

geogebra에서 그렸다.

위 그림의 주황색 선은 원본 선을 1만큼 embolden 시킨게 된다. 

우리의 목적은 B점을 K점으로 이동시키는 것이다.

그러기 위해 BK(shift vector)를 구해야한다.

이렇게 1만큼 두껍게 만드는 shift vector를 SHIFT vector 라고 하자.

strength만큼 두껍게만들고싶다면 SHIFT에 strength만큼 곱해서 원래 점(B)에 더해주기만 하면된다.

 

그럼 SHIFT를 구해보자.  

1. 방향을 구한다.

2. 크기를 구한다.

 

 

1. 방향을 구한다.

코드에서 AB가 in, BC가 out이다. (나중에는 BD,BH로 바뀌긴 한다만)

BD가 in의 unit normal. BH가 out의 unit normal.

 

in, out의 unit normal vector(단위법선벡터)를 구한다. 이는 이후 계산을 쉽게 하기 위함인듯?

(코드에서는 in, out을 unit normal vector로 '치환' 함)

normal vector의 방향은 path 방향의 바깥으로 뻗어나가도록 구한다.

( ex. 'ㅇ'을 그린다고 했을 때 , 반시계 방향으로 path를 그렸다면, 법선벡터는 시계 방향 회전.)

 

그럼 이 두 법선 벡터를 더하면 끝.

 

$$ shift = \vec{BD} + \vec{BH} $$

 

방향은 쉽게 구할 수 있다.

이제 크기를 구해보자.

 


2. 크기를 구한다.

 

BK의 길이를 구해보자.

BDK 삼각형을 보자.

직각삼각형이다.

 

$$ \frac{|BD|} {|BK|} = \sin(BKD) = \cos(DBK) $$ 

 

BK는 크기가 같은 두 벡터 BD와 BH를 합성한 것이기 때문에

두 벡터의 정중앙으로 나아갈것이다.

 

따라서 BD,BH 사이각을 θ라고 한다면, 각(DBK)는 θ/2가 된다.

 

$$|BK| = \frac{1}{\cos\frac{\theta}{2}}$$

 

삼각함수 반각공식 $$\cos^2(\frac{x}{2}) = \frac{1 + \cos x}{2}$$

 

정리하면

$$ |BK| = \frac{1}{\sqrt{\frac{1 + \cos \theta}{2}}} = \sqrt{ \frac{2}{1+\cos \theta}}$$

 

BD, BH를 알기 때문에 이제 BK의 길이를 알수있다.

 

2번도 끝.


 

하지만 FreeType 코드는 여기서 한발 더나감.

$$SHIFT = \vec{BK} $$

라고 했을때,

 

$$ SHIFT = shift \times x $$

|SHIFT| 는 2번에서 구했고, shift는 1번에서 구했다.

x를 구해보자.

 

1번에서 구한 shift = BD + BH

$$ BD = (a,\ b),\ BH = (c,\ d) $$

$$ shift = (a+c, b+d) $$

 

$$ |shift| = \sqrt{(a+c)^2 + (b+d)^2} $$

$$ |shift| = \sqrt{ a^2 + c^2 +2ac + b^2 + d^2 + 2bd } $$

$$ |shift| = \sqrt{ a^2 + b^2 + c^2 + d^2 + 2ac + 2bd } $$

$$ |shift| = \sqrt{ 1 + 1 + 2\cos\theta } $$

 

2번에서 구한 |SHIFT|

$$ |BK| = |SHIFT| = \sqrt{ \frac{2}{1 + \cos\theta} } $$

 

$$ x = \frac{|SHIFT|}{|shift|} = \frac{ \sqrt{ \frac{2}{1 + \cos\theta} } } { \sqrt{ 2 + 2\cos\theta } } $$

$$ = \sqrt{ \frac{2}{1 + \cos\theta}} \times \sqrt{ \frac{1}{2(1+\cos\theta)} } $$

$$ = \sqrt{ \frac{1}{ (1 + \cos\theta)^2} } $$

$$ = \frac {1} { 1 + \cos\theta } $$

 

$$ SHIFT= shift \times \frac {1} {1 + \cos\theta} $$

costθ는 BD, BH을 내적하면 나온다.

BD = (a, b), BH = (c, d)라고 했을 때,

$$ BD \cdot BH = a \times c + b \times d = |BD| \times |BH| \times \cos\theta = 1 \times 1 \times \cos\theta $$

$$ \cos\theta = a \times c + b \times d $$

 

$$ SHIFT= shift \times \frac {1} {1 + ac + bd} $$

 

3. point 이동.

shifted_point = point + SHIFT

 

끝!

 


점 B는 AB, BC의 교점

AB, BC를 embolden시킨다. = 평행이동시킨다.

교점 K를 찾는다.

=> embolden 시키려면 점 B를 점 K로 이동시키자.

 

 

근데 이런식으로하면 각이 뾰족한경우.. AB와 BC가 거의 평행한 경우에는 점 B가 저~멀리 안드로메다로..

혹은 완전 평행한경우에는 AB, BC를 평행이동시켰을때 교점이 생기지않음...

그래서 그 예외처리한게

if ( d < -10000L) 인데 

이거는 다음에 마저 올려야겟다.. 

 

Posted by outshine90
,