outline_cocos source

ÄÚÄÚ½º¿¡ ÀÖ´Â ¿Ü°û¼±À» Ç¥½ÃÇÏ´Â Äڵ带 ºÐ¼®ÇÑ´Ù.

Ãâ·ÂµÈ À̹ÌÁö¸¦ ¸ÕÀú º¸ÀÚ.


TestOutline2 ÇÔ¼ö¿¡¼­ FreeTypeÀ» ÃʱâÈ­ ÇÏ°í  ÆùÆ®¸¦ ±×¸°´Ù.
int TestOutline2()
{
    if (FT_Init_FreeType(&s_library))
        return -1;

    //ÆùÆ® Àбâ
    FT_Face face;
    //int error = FT_New_Face(s_library,"³ª´®°íµñ.ttf", 0, &face);
    int error = FT_New_Face(s_library,"c:\\windows\\fonts\\arial.ttf", 0,&face);

    //ÆùÆ® Å©±â ¼³Á¤
    error = FT_Set_Char_Size(face, 0, 256*64, 96, 96);

    Word word;
    word.Reset();
    word.m_code = TEXT('A');

    DrawFontBorder(face, word, 0xFF0000,  0xFF, 10);

    //face Á¤¸®
    FT_Done_Face(face);

    // ¶óÀ̺귯¸® Á¤¸®
    FT_Done_FreeType(s_library);

    if(word.m_buffer)
    {
        WriteTGA("test_outline2.tga",  word.m_buffer, word.m_width, word.m_height);
        delete [] word.m_buffer;
        word.m_buffer = NULL;
    }
    return 0;
}

DrawFontBorder ÇÔ¼ö¿¡¼­ ÆùÆ®¸¦ ±×¸°´Ù.

bool DrawFontBorder(FT_Face& face, Word& word, unsigned int color1, unsigned  int color2, unsigned  int border)
border´Â ¾Æ¿ô¶óÀÎ µÎ²²ÀÌ´Ù.

bool DrawFontBorder(FT_Face& face, Word& word, unsigned  int color1, unsigned  int color2, unsigned  int border)
{
    FT_UInt index = FT_Get_Char_Index(face, FT_ULong(word.m_code));
    if (!index)
         return  false;

    if (FT_Load_Glyph(face, index, FT_LOAD_NO_BITMAP))
         return  false;

    FT_Glyph glyph;
    if (FT_Get_Glyph(face->glyph, &glyph))
         return  false;

    FT_Stroker stroker;
    if (FT_Stroker_New(s_library, &stroker))
         return  false;

    FT_Stroker_Set(stroker, (int)(border * 64), FT_STROKER_LINECAP_ROUND,  FT_STROKER_LINEJOIN_ROUND, 0);

    if (FT_Glyph_StrokeBorder(&glyph, stroker, 0, 1))
         return  false;

    FT_Outline *outline = &reinterpret_cast<FT_OutlineGlyph>(glyph)->outline;

    FT_BBox bbox;
    FT_Glyph_Get_CBox(glyph, FT_GLYPH_BBOX_GRIDFIT, &bbox);
   
    int width = (bbox.xMax - bbox.xMin) >> 6;  // 64·Î ³ª´©±â
    int rows  = (bbox.yMax - bbox.yMin) >> 6;

    FT_Bitmap *bitmap = &face->glyph->bitmap;

    //¸®ÅÏÇÒ Á¤º¸¸¦ ¼³Á¤ÇÑ´Ù.
    word.m_width    = width;
    word.m_height   = rows;
    word.m_drawX    = face->glyph->metrics.horiBearingX >> 6;
    word.m_drawY    = face->glyph->metrics.horiBearingY >> 6;
    word.m_advanceX = face->glyph->metrics.horiAdvance  >> 6;
    word.m_buffer =  new unsigned  char[word.m_width * word.m_height * 4];
    memset(word.m_buffer, 0, word.m_width * word.m_height * 4);

    unsigned  char* buffer = word.m_buffer;
       
    FT_Raster_Params  params;
    FT_Bitmap bmp;

    //¿Ü°û¿ë ÆùÆ®¸¦ ±×¸°´Ù.
    bmp.buffer =  new unsigned  char[width * rows];
    memset(bmp.buffer, 0, width * rows);
    bmp.width       = width;
    bmp.rows        = rows;
    bmp.pitch       = width;
    bmp.pixel_mode  = FT_PIXEL_MODE_GRAY;
    bmp.num_grays   = 256;

    memset(& params, 0,  sizeof ( params));
    params.source   = outline;
    params.target   = &bmp;
    params.flags    = FT_RASTER_FLAG_AA;
    FT_Outline_Translate(outline,-bbox.xMin,-bbox.yMin);
    FT_Outline_Render(s_library, outline, &params); //rasterize font
    unsigned  char* buffer1 = bmp.buffer;
   
    //³»ºÎ ÆùÆ®¸¦ ±×¸°´Ù.
    FT_BBox bbox_in;
    FT_Glyph glyph_fg;
    FT_Get_Glyph(face->glyph, &glyph_fg);
    FT_Glyph_Get_CBox(glyph_fg, FT_GLYPH_BBOX_GRIDFIT,&bbox_in);

    bmp.buffer =  new unsigned  char[width * rows];
    memset(bmp.buffer, 0, width * rows);
    bmp.width       = width;
    bmp.rows        = rows;
    bmp.pitch       = width;
    bmp.pixel_mode  = FT_PIXEL_MODE_GRAY;
    bmp.num_grays   = 256;
    outline = &reinterpret_cast<FT_OutlineGlyph>(glyph_fg)->outline;
    memset(& params, 0,  sizeof ( params));
    params.source   = outline;
    params.target   = &bmp;
    params.flags = FT_RASTER_FLAG_AA;
    FT_Outline_Translate(outline,-bbox.xMin,-bbox.yMin);
    FT_Outline_Render(s_library, outline, &params); //rasterize font
    unsigned  char* buffer2 = bmp.buffer;

    int pitch = width;
    for ( int yy = 0; yy < rows; ++yy)
    {
        for ( int xx = 0; xx < width; ++xx)
        {
            //¿Ü°û¼± ºñÆ®¸Ê ÆùÆ®¸¦ ÁöÁ¤ Ä®¶ó·Î ±×¸°´Ù.
            int si = yy * word.m_width * 4 + xx * 4;
            int alpha1 = buffer1[yy * pitch + xx];

            unsigned  char sr = (color1 & 0xFF0000) >> 16,
                          sg = (color1 & 0xFF00  ) >> 8,
                          sb = (color1 & 0xFF    );

            unsigned  char dr = (color2 & 0xFF0000) >> 16,
                          dg = (color2 & 0xFF00  ) >> 8,
                          db = (color2 & 0xFF    );

            if (alpha1)
            {
                buffer[si + 0] = dr;
                buffer[si + 1] = dg;
                buffer[si + 2] = db;
                buffer[si + 3] =  alpha1;
            }

            //³»ºÎ ºñÆ®¸Ê ÆùÆ®¸¦ ÁöÁ¤ Ä®¶ó·Î ±×¸°´Ù.
            int alpha2 = buffer2[yy * pitch + xx];
            if (alpha2)
            {
                buffer[si + 0] = dr + ( sr - dr) * alpha2 / 255.0f;
                buffer[si + 1] = dg + ( sg - dg) * alpha2 / 255.0f;
                buffer[si + 2] = db + ( sb - db) * alpha2 / 255.0f;
                buffer[si + 3] =  min(255, alpha1 + alpha2);
            }
        }
    }

    delete [] buffer1;
    delete [] buffer2;

    FT_Stroker_Done(stroker);
    FT_Done_Glyph(glyph);

    return  true;
}

ÇÔ¼ö¿¡¼­ »ç¿ëµÇ´Â ¸Å°³º¯¼ö Word´Â ´ÙÀ½°ú °°´Ù.
struct Word
{
    FT_ULong m_code;
    int m_width;
    int m_height;
    int m_drawX;
    int m_drawY;
    int m_advanceX;
    unsigned char* m_buffer;

    void Reset()
    {
        m_code = 0;
        m_width = 0;
        m_height = 0;
        m_drawX = 0;
        m_drawY = 0;
        m_advanceX = 0;  //FT_Glyph_MetricsÀÇ horiAdvance °ªÀÌ´Ù. ¼öÆò ÁøÇàÆø
        m_buffer = NULL;
    }
};

ÁøÇàÆø :  ÇöÀç ±ÛÀÚ¿¡¼­ ´ÙÀ½ ±ÛÀÚ¸¦ ±×¸®±â À§ÇØ À̵¿ÇØ¾ß ÇÒ °Å¸®ÀÌ´Ù.

1. ±Û¸®ÇÁ ¿ÀºêÁ§Æ® °¡Á®¿À±â
¹®ÀÚ ÄÚµå·Î ±Û¸®ÇÁ À妽º¸¦ ±¸ÇÑ´Ù.
±Û¸®ÇÁ À妽º·Î ±Û¸®ÇÁ¸¦ ·ÎµùÇÑ´Ù.
·ÎµùÇÑ ±Û¸®ÇÁ ½½·Ô¿¡¼­ ±Û¸®ÇÁ ¿ÀºêÁ§Æ®¸¦ »ý¼ºÇÑ´Ù.
    FT_UInt index = FT_Get_Char_Index(face, FT_ULong(word.m_code));
    if (!index)
         return  false;

    if (FT_Load_Glyph(face, index, FT_LOAD_NO_BITMAP))
         return  false;

    FT_Glyph glyph;
    if (FT_Get_Glyph(face->glyph, &glyph))
         return  false;

¹®ÀÚ Äڵ忡 ÇØ´çÇÏ´Â ±Û¸®ÇÁ À妽º¸¦ ±¸ÇÑ´Ù.
FT_UInt FT_Get_Char_Index( FT_Face   face, FT_ULong  charcode )

ÇØ´ç ±Û¸®ÇÁ À妽º¸¦ face¿¡ ·ÎµùÇÑ´Ù.
FT_Error FT_Load_Glyph( FT_Face   face, FT_UInt   glyph_index, FT_Int32  load_flags )

±Û¸®ÇÁ ½½·ÔÀ¸·Î ºÎÅÍ ±Û¸®ÇÁ (º¤ÅÍ)À̹ÌÁö ¿ÀºêÁ§Æ®¸¦ °¡Á®¿Â´Ù.   FT_Done_Glyph·Î ÇØÁ¦ µÇ¾î¾ß ÇÑ´Ù.
FT_Error FT_Get_Glyph( FT_GlyphSlot  slot, FT_Glyph *aglyph )

2. ¿Ý°û¼±À» ±×¸®±â À§ÇØ ½ºÆ®·ÎÅ© °´Ã¼¸¦ ¸¸µé°í FT_Outline °´Ã¼¸¦ ¾ò¾î¿Â´Ù.
    FT_Stroker stroker;
    if (FT_Stroker_New(s_library, &stroker))
         return  false;

    FT_Stroker_Set(stroker, (int)(border * 64), FT_STROKER_LINECAP_ROUND,  FT_STROKER_LINEJOIN_ROUND, 0);

    if (FT_Glyph_StrokeBorder(&glyph, stroker, 0, 1))
         return  false;

    FT_Outline *outline = &reinterpret_cast<FT_OutlineGlyph>(glyph)->outline;

FT_Stroker_Set : border ¶óÀÎ µÎ²²ÀÌ´Ù.
FT_Glyph_StrokeBorder :  glyph¿¡ Stroker¸¦ ¼³Á¤ÇÑ´Ù.
FT_Outline_Render¿¡ ÇÊ¿äÇÑ FT_Outline °´Ã¼¸¦ ±¸ÇÑ´Ù.

3. ¸®ÅÏÇÒ Á¤º¸¸¦ ¼³Á¤ÇÑ´Ù.
    FT_BBox bbox;
    FT_Glyph_Get_CBox(glyph, FT_GLYPH_BBOX_GRIDFIT, &bbox);
   
    int width = (bbox.xMax - bbox.xMin) >> 6;  // 64·Î ³ª´©±â
    int rows  = (bbox.yMax - bbox.yMin) >> 6;

    FT_Bitmap *bitmap = &face->glyph->bitmap;

    //¸®ÅÏÇÒ Á¤º¸¸¦ ¼³Á¤ÇÑ´Ù.
    word.m_width    = width;
    word.m_height   = rows;
    word.m_drawX    = face->glyph->metrics.horiBearingX >> 6;
    word.m_drawY    = face->glyph->metrics.horiBearingY >> 6;
    word.m_advanceX = face->glyph->metrics.horiAdvance  >> 6;
    word.m_buffer =  new unsigned  char[word.m_width * word.m_height * 4];
    memset(word.m_buffer, 0, word.m_width * word.m_height * 4);

    unsigned  char* buffer = word.m_buffer;

¿©±â¼­ FT_Glyph_Get_CBox´Â ¿Ü°û¼±À» ±×¸®±â À§ÇÑ ¹Ú½º¸¦ ±¸ÇÏ°í bbox´Â ¿Ü°û¼±À» ±×¸±¶§ »ç¿ëµÈ´Ù.

FT_GLYPH_BBOX_PIXELS·Î ÁöÁ¤Çϸé 64·Î ³ª´²ÁÙ ÇÊ¿ä°¡ ¾ø´Âµ¥ ¿Ö FT_GLYPH_BBOX_GRIDFITÀ¸·Î ÇÏ´ÂÁö´Â ÀÌÇØ°¡ ¾ÈµÊ
int width = (bbox.xMax - bbox.xMin) >> 6; 
int rows  = (bbox.yMax - bbox.yMin) >> 6;

4. ¿Ü°û¿ë ÆùÆ®¸¦ ±×¸°´Ù.
    FT_Raster_Params  params;
    FT_Bitmap bmp;

    //¿Ü°û¿ë ÆùÆ®¸¦ ±×¸°´Ù.
    bmp.buffer =  new unsigned  char[width * rows];
    memset(bmp.buffer, 0, width * rows);
    bmp.width       = width;
    bmp.rows        = rows;
    bmp.pitch       = width;
    bmp.pixel_mode  = FT_PIXEL_MODE_GRAY;
    bmp.num_grays   = 256;

    memset(& params, 0,  sizeof ( params));
    params.source   = outline;
    params.target   = &bmp;
    params.flags    = FT_RASTER_FLAG_AA;
    FT_Outline_Translate(outline,-bbox.xMin,-bbox.yMin);
    FT_Outline_Render(s_library, outline, &params); //rasterize font
    unsigned  char* buffer1 = bmp.buffer;

FT_Outline_Translate : ÆùÆ®¸¦ ±×¸±¶§ bboxÀÇ ÃÖ¼Ò°ª¸¸Å­ À̵¿ ½ÃŲ´Ù.
FT_Outline_Render : ½ºÄµ º¯È¯À» »ç¿ëÇÏ¿© À±°û¼±À» ºñÆ®¸ÊÀ» Ãâ·ÂÇÑ´Ù.

5. ³»ºÎ ÆùÆ®¸¦ ±×¸°´Ù.
    //³»ºÎ ÆùÆ®¸¦ ±×¸°´Ù.
    FT_BBox bbox_in;
    FT_Glyph glyph_fg;
    FT_Get_Glyph(face->glyph, &glyph_fg);
    FT_Glyph_Get_CBox(glyph_fg, FT_GLYPH_BBOX_GRIDFIT,&bbox_in);

    bmp.buffer =  new unsigned  char[width * rows];
    memset(bmp.buffer, 0, width * rows);
    bmp.width       = width;
    bmp.rows        = rows;
    bmp.pitch       = width;
    bmp.pixel_mode  = FT_PIXEL_MODE_GRAY;
    bmp.num_grays   = 256;
    outline = &reinterpret_cast<FT_OutlineGlyph>(glyph_fg)->outline;
    memset(& params, 0,  sizeof ( params));
    params.source   = outline;
    params.target   = &bmp;
    params.flags = FT_RASTER_FLAG_AA;
    FT_Outline_Translate(outline,-bbox.xMin,-bbox.yMin);
    FT_Outline_Render(s_library, outline, &params); //rasterize font
    unsigned  char* buffer2 = bmp.buffer;

6. ÃÖÁ¾ÀûÀ¸·Î ¾Æ¿ì¶óÀÎÀÌ ÀÖ´Â Ä®¶ó ºñÆ®¸ÊÀ» Ãâ·ÂÇÑ´Ù.
    int pitch = width;
    for ( int yy = 0; yy < rows; ++yy)
    {
        for ( int xx = 0; xx < width; ++xx)
        {
            //¿Ü°û¼± ºñÆ®¸Ê ÆùÆ®¸¦ ÁöÁ¤ Ä®¶ó·Î ±×¸°´Ù.
            int si = yy * word.m_width * 4 + xx * 4;
            int alpha1 = buffer1[yy * pitch + xx];

            unsigned  char sr = (color1 & 0xFF0000) >> 16,
                          sg = (color1 & 0xFF00  ) >> 8,
                          sb = (color1 & 0xFF    );

            unsigned  char dr = (color2 & 0xFF0000) >> 16,
                          dg = (color2 & 0xFF00  ) >> 8,
                          db = (color2 & 0xFF    );

            if (alpha1)
            {
                buffer[si + 0] = dr;
                buffer[si + 1] = dg;
                buffer[si + 2] = db;
                buffer[si + 3] =  alpha1;
            }

            //³»ºÎ ºñÆ®¸Ê ÆùÆ®¸¦ ÁöÁ¤ Ä®¶ó·Î ±×¸°´Ù.
            int alpha2 = buffer2[yy * pitch + xx];
            if (alpha2)
            {
                buffer[si + 0] = dr + ( sr - dr) * alpha2 / 255.0f;
                buffer[si + 1] = dg + ( sg - dg) * alpha2 / 255.0f;
                buffer[si + 2] = db + ( sb - db) * alpha2 / 255.0f;
                buffer[si + 3] =  min(255, alpha1 + alpha2);
            }
        }
    }

word.m_width¿Í width´Â °°Àº °ªÀÌ´Ù.
buffer : ºñÆ®¸ÊÀ» Ãâ·ÂÇÒ ¹öÆÛ
buffer1: ¿Ü°û¼±¿ë 256 ±×·¹ÀÌ ¹öÆÛ
buffer2: ³»ºÎ 256 ±×·¹ÀÌ ¹öÆÛ

7. ¸®¼Ò½º¸¦ ÇØÁ¦ÇÑ´Ù.
    delete [] buffer1;
    delete [] buffer2;

    FT_Stroker_Done(stroker);
    FT_Done_Glyph(glyph);

´Ù¿î·Îµå: outline_cocos.cpp

ÂüÁ¶)
http://freetype.sourceforge.net/freetype2/docs/tutorial/step2.html
http://www.soen.kr/lecture/library/freetype/ft2.htm
http://www.soen.kr/lecture/library/freetype/ft3.htm

http://www.cppblog.com/shly/archive/2013/12/07/204638.html
http://www.cppblog.com/wc250en007/archive/2011/07/13/150812.html