////////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2006-2008 MStar Semiconductor, Inc.
// All rights reserved.
//
// Unless otherwise stipulated in writing, any and all information contained
// herein regardless in any format shall remain the sole proprietary of
// MStar Semiconductor Inc. and be kept in strict confidence
// ("MStar Confidential Information") by the recipient.
// Any unauthorized act including without limitation unauthorized disclosure,
// copying, use, reproduction, sale, distribution, modification, disassembling,
// reverse engineering and compiling of the contents of MStar Confidential
// Information is unlawful and strictly prohibited. MStar hereby reserves the
// rights to any and all damages, losses, costs and expenses resulting therefrom.
//
////////////////////////////////////////////////////////////////////////////////
#define _MAPI_GFX2D_C
///////////////////////////////////////////////////////////////////////////////
/// @file   mapi_gfx2d_mvf_base.c
/// @author MStar Semiconductor Inc.
/// @brief  mapi - graphics 2D - MVF - Vector Font Engine render. Reference "smooth" render of FreeType2 library
///////////////////////////////////////////////////////////////////////////////
//------------------------------------------------------------------------------
// Include Files
//------------------------------------------------------------------------------
//public header files first
#include <directfb.h>
#include "mstar_mvf.h"


//------------------------------------------------------------------------------
// Local Defines
//------------------------------------------------------------------------------
#define THIS_TAG DBG_TAG('2','D','V','B')


#ifdef MVF_DEBUG

#define MVF_BASE_DEBUG(x)   x

#else //MVF_DEBUG

#define MVF_BASE_DEBUG(x) //x

#endif //MVF_DEBUG


//------------------------------------------------------------------------------
// Macros
//------------------------------------------------------------------------------


//////////////////////////////////////////////////////////////////////////
// define file structure in little endian stream....

#define MVF_NEXT_ULONG(p) p += sizeof(U32)
#define MVF_NEXT_LONG(p) p += sizeof(S32)
#define MVF_NEXT_U24(p) p += 3
#define MVF_NEXT_S24(p) p += 3
#define MVF_NEXT_USHORT(p) p += sizeof(U16)
#define MVF_NEXT_SHORT(p) p += sizeof(S16)
#define MVF_NEXT_BYTE(p) p += sizeof(U8)

#define MVF_PEEK_USHORT(p) ((((U16)p[1])<<8)|p[0])
#define MVF_PEEK_SHORT(p) ((S16)MVF_PEEK_USHORT(p))
#define MVF_PEEK_U24(p) ((((U32)p[2])<<16)|(p[1]<<8)|p[0])
#define MVF_PEEK_S24(p) ((S32)MVF_PEEK_U24(p))
#define MVF_PEEK_ULONG(p) ((((U32)p[3])<<24)|(p[2]<<16)|(p[1]<<8)|p[0])
#define MVF_PEEK_LONG(p) ((S32)MVF_PEEK_ULONG(p))
#define MVF_PEEK_BYTE(p) (*p)



//------------------------------------------------------------------------------
// Global Variables
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
// Local Variables
//------------------------------------------------------------------------------


//------------------------------------------------------------------------------
// Local Function Prototypes
//------------------------------------------------------------------------------


//------------------------------------------------------------------------------
// Public Function Prototypes
//------------------------------------------------------------------------------


//-------------------------------------------------------------------------------------------------
/// Load a font face into memory
/// @param  file_base    \b IN: MVF raw data start address
/// @param  file_limit   \b IN: MVF raw data end address (exclude)
/// @param  *aface        \b OUT: font face handle
/// @return ==0 : successful
/// @return !=0 : fail
//-------------------------------------------------------------------------------------------------
MVF_Error MVF_New_Face(
                      U8 *  file_base,
                      U8 *  file_limit, //range: [file_base, file_limit)
                      MVF_Face *        aface)
{
    U8 * pos;
    //U8 * end;
    S16 i;
    U16 n, m, flags, touch_count, hint_sizes;

    U16 advance_x;
    U16 metrics_height;
    S16 ascender;
    S16 descender;

    U16 max_outline_size = 1, outline_size; //at least one byte, don't allocate zero

    D_ASSERT( file_base != 0 );
    D_ASSERT( file_limit > file_base );
    D_ASSERT( aface != 0 );

    MVF_BASE_DEBUG(printf("MVF_New_Face() 1, base=%x\n", file_base));

    aface->max_advance = aface->metrics_height = 0;
    aface->ascender = aface->descender = 0;

    pos = file_base;
    //end = file_base + file_size;
    if ( MVF_PEEK_USHORT(pos) != 0x0710 ) //magic number
        return -2; //file format error
    MVF_NEXT_USHORT(pos);

    aface->file_version = MVF_PEEK_USHORT(pos);
    if ( aface->file_version != MVF_FILE_FORMAT_VERSION && //file format version
        aface->file_version != MVF_FILE_FORMAT_VERSION_7052) //backward compatible
        return -12; //file format error
    MVF_NEXT_USHORT(pos);

    aface->file_base = file_base;
    aface->file_limit = file_limit;
    aface->attach_data_base = aface->attach_data_limit = 0;
    aface->num_glyphs = MVF_PEEK_SHORT(pos);
    MVF_NEXT_SHORT(pos);

    D_ASSERT( aface->num_glyphs > 0 );
    aface->glyphs = (MVF_Glyph*) MVF_MALLOC(sizeof(MVF_Glyph)*aface->num_glyphs);
    if ( !aface->glyphs )
        return -1; //out of memory....

    for (i = 0; i < aface->num_glyphs; i++)
    {
        if ( pos >= file_limit )
            return -3; //end of file...

        //unicode
        aface->glyphs[i].unicode = MVF_PEEK_USHORT(pos);
        MVF_BASE_DEBUG(printf("parse unicode = %d\n", aface->glyphs[i].unicode));
        MVF_NEXT_USHORT(pos);

        //outline data....
        aface->glyphs[i].outline_data = pos;
        //pos += 4 * (sizeof(S16)); //width, height, x_shift, y_shift
        m = 0;
        n = MVF_PEEK_USHORT(pos); //n_point
        touch_count = 0;
        MVF_NEXT_USHORT(pos);
        if ( n > 0 )
        {
            flags = MVF_PEEK_BYTE(pos); //use16
            MVF_NEXT_BYTE(pos);
            if (flags & MVF_OUTLINE_X_USE16) //X
                pos += n * (sizeof(S16));
            else
                pos += n * (3); //use 24bits
            if (flags & MVF_OUTLINE_Y_USE16) //Y
                pos += n * (sizeof(S16));
            else
                pos += n * (3); //use 24bits
            pos += n * (sizeof(U8)); //tags
            pos += sizeof(S32)*2; //min_x,min_y
            if ( n < 256 ) //can present in a byte...
            {
                m = MVF_PEEK_BYTE(pos); //n_cornor
                MVF_NEXT_BYTE(pos);
                pos += m * (sizeof(U8));
            }
            else
            {
                m = MVF_PEEK_USHORT(pos); //n_cornor
                MVF_NEXT_USHORT(pos);
                pos += m * (sizeof(S16));
            }
#if MVF_HINTING_ENABLE

            hint_sizes = MVF_PEEK_BYTE(pos); //number for hint font sizes...
            MVF_NEXT_BYTE(pos);
            pos += sizeof(U8)*hint_sizes; // list of size...
            pos += sizeof(U8)*hint_sizes; // list of advance.x...
            touch_count = MVF_PEEK_USHORT(pos);
            MVF_NEXT_USHORT(pos);
            pos += sizeof(S16)*touch_count*hint_sizes; //touch points..

#endif

        }
#if MVF_HINTING_ENABLE
        outline_size = n * (2*sizeof(MVF_Vector)+sizeof(MVF_HintPoint)) + //orig outline + scaled outline
            m * (sizeof(S16)) +
            touch_count * sizeof(S16);
#else
        outline_size = n * (2*sizeof(MVF_Vector)) + //orig outline + scaled outline
            m * (sizeof(S16));
#endif
        if ( outline_size > max_outline_size )
            max_outline_size = outline_size;

        advance_x = MVF_PEEK_USHORT(pos);
        MVF_NEXT_USHORT(pos);
        metrics_height = MVF_PEEK_USHORT(pos);
        MVF_NEXT_USHORT(pos);
        ascender = MVF_PEEK_SHORT(pos);
        MVF_NEXT_SHORT(pos);
        descender = MVF_PEEK_SHORT(pos);
        MVF_NEXT_SHORT(pos);

	 if(advance_x > aface->max_advance)
	 	aface->max_advance = advance_x;
	 if(metrics_height > aface->metrics_height)
	 	aface->metrics_height = metrics_height;
	 if(ascender > aface->ascender)
	 	aface->ascender = ascender;
	 if(descender > aface->descender)
	 	aface->descender = descender;

        //pos += 4 * sizeof(S16); //advance.x, metrics_height, ascender, descender

        //kerning data.....
        aface->glyphs[i].kerning_data = pos;
        n = MVF_PEEK_USHORT(pos); //n_kerning_pair
        MVF_NEXT_USHORT(pos);
        pos += n * (sizeof(S16)+sizeof(S16)); //unicode,delta_x

    }

    //2007/10/19: new feature: support attach data if the terminal contains 4 bytes zero...
    if (aface->file_version == MVF_FILE_FORMAT_VERSION &&
        pos+sizeof(U32) < aface->file_limit &&
        MVF_PEEK_ULONG(pos) == 0)
    {
        MVF_NEXT_ULONG(pos);
        aface->attach_data_base = pos;
        aface->attach_data_limit = aface->file_limit;

    }
    aface->outline_buffer_start = (U8*) MVF_MALLOC((max_outline_size*8)/7); //+14% for safe...
    if ( !aface->outline_buffer_start )
        return -1; //out of memory....

    return 0; //ok
}

//-------------------------------------------------------------------------------------------------
/// Free a font face handle
/// @param  face         \b IN: font face handle
/// @return ==0 : successful
/// @return !=0 : fail
//-------------------------------------------------------------------------------------------------
void MVF_Done_Face(MVF_Face * face)
{
    D_ASSERT(face != 0);

    if (face->glyphs)
        MVF_FREE(face->glyphs);
    if (face->outline_buffer_start)
        MVF_FREE(face->outline_buffer_start);
    MVF_MEM_ZERO(face, sizeof(MVF_Face));
}

#if MVF_HINTING_ENABLE
//-------------------------------------------------------------------------------------------------
/// parse hinting touched points (internal use)
/// @param  outline           \b IN: original outline data
/// @param  hindex            \b IN: index of hinting sizes
/// @param  hpoints           \b OUT: touched points coodinates
/// @return ==0 : successful
/// @return !=0 : fail
//-------------------------------------------------------------------------------------------------
void _mvf_load_hinting_touch_points(MVF_Outline * outline,
                 S16 hindex,
                 //U16 touch_count,
                 MVF_HintPoint * hpoints ) //[OUT]
{
    S16 i;
    U8 * pos;

    D_ASSERT( outline != 0 );
    D_ASSERT( outline->hinting_data != 0 );
    D_ASSERT( hpoints != 0 );

    pos = outline->hinting_data + sizeof(S16)*outline->hinting_n_touch_points*hindex;

    for ( i = 0; i < outline->n_points; i++)
    {
        if (hpoints[i].touch_flags & MVF_HINTING_X_TOUCH_TAG)
        {
            hpoints[i].x = MVF_PEEK_SHORT(pos);
            MVF_NEXT_SHORT(pos);
        }
        if (hpoints[i].touch_flags & MVF_HINTING_Y_TOUCH_TAG)
        {
            hpoints[i].y = MVF_PEEK_SHORT(pos);
            MVF_NEXT_SHORT(pos);
        }
    }

}

#endif


//-------------------------------------------------------------------------------------------------
/// get kerning pair distance
/// @param  face              \b IN: font face handle
/// @param  left_char         \b IN: left char unicode
/// @param  right_char        \b IN: right char unicode
/// @param  font_size         \b IN: font pixel size
/// @param  *akerning         \b OUT: offset distance (only use x)
/// @return ==0 : successful
/// @return !=0 : fail
//-------------------------------------------------------------------------------------------------
MVF_Error MVF_Get_Kerning( MVF_Face *     face,
                  U32     left_glyph_id,
                  U32     right_glyph_id,
                  U16     font_size,
                  MVF_Vector *    akerning )
{
    int glyph_id, i, n;
    //S16 L, R, M;
    MVF_Error err;
    U8 * pos;

    D_ASSERT( face != 0 );
    D_ASSERT( face->glyphs != 0 );
    D_ASSERT( akerning != 0 );

    /*/binary search for matched unicode
    L = 0;
    R = face->num_glyphs - 1;
    while ( L < R )
    {
        M = (L+R)/2;
        index = face->glyphs[M].unicode;
        if (index < left_char)
            L = M+1;
        else if (index > left_char)
            R = M-1;
        else
        {
            L = M;
            break;
        }
    }
    index = face->glyphs[L].unicode;
    if ( index != left_char )
        return -3; //char code not found...
    */

  //  if ((err = MVF_Query_Glyph_ID(face, left_char, &index) ) != 0)
   //     return err;

    akerning->x = akerning->y = 0;

    pos = face->glyphs[left_glyph_id].kerning_data;
    n = MVF_PEEK_USHORT(pos);
    if ( n == 0 ) //no kerning pairs....
        return 0;
    MVF_NEXT_USHORT(pos);
    for (i = 0; i < n; i++)
    {
        U32 right_char= (U32)MVF_PEEK_USHORT(pos);
        MVF_NEXT_USHORT(pos);
       if ((err = MVF_Query_Glyph_ID(face, right_char, &glyph_id) ) != 0)
              return err;

        if (glyph_id == right_glyph_id)
        {
            akerning->x = MVF_BASE_EM_ROUND( MVF_PEEK_SHORT(pos), font_size );
            return 0;
        }
        MVF_NEXT_SHORT(pos);
    }
    return 0; //no matched kerning....
}


#if MVF_CACHE_OUTLINE_ENABLE

  typedef struct  MVF_CacheOutlineRec_
  {
    //BOOLEAN   used;
    U8 * raw_start; //raw outline data pointer

    MVF_Outline outline;

  } MVF_CacheOutline;

static U8 * _s_cache_outline_buff = 0; //bitmap buffer
static MVF_CacheOutline * _s_cache_outline_pool = 0; //array of small bitmaps
static U16 _s_cache_outline_last = 0;

#endif

//-------------------------------------------------------------------------------------------------
/// Find glyph ID with unicode
/// @param  face              \b IN: font face handle
/// @param  unicode           \b IN: char coce (unicode)
/// @param  *glyph_id          \b OUT: glyph ID
/// @return ==0 : successful
/// @return !=0 : fail
//-------------------------------------------------------------------------------------------------
MVF_Error MVF_Query_Glyph_ID( MVF_Face *   face,
                 int   unicode, int * glyph_id)
{
    int index;
    S32 L, R, M;

    D_ASSERT( face != 0 );
    D_ASSERT( glyph_id != 0 );
    D_ASSERT( face->glyphs != 0 );

    //binary search for matched unicode
    L = 0;
    R = face->num_glyphs - 1;
    while ( L < R )
    {
        M = (L+R)/2;
        index = face->glyphs[M].unicode;
        if (index < unicode)
            L = M+1;
        else if (index > unicode)
            R = M-1;
        else
        {
            L = M;
            break;
        }
    }
    index = face->glyphs[L].unicode;
    if ((U32) index != unicode )
        return -3; //char code not found...

    *glyph_id = L;
    return 0;
}

//-------------------------------------------------------------------------------------------------
/// Load a outline of a specified glyph ID
/// @param  face              \b IN: font face handle
/// @param  glyph_id          \b IN: glyph ID (the serial number in the font file)
/// @param  *outline          \b OUT: outline and metrics data
/// @return ==0 : successful
/// @return !=0 : fail
//-------------------------------------------------------------------------------------------------
MVF_Error MVF_Load_Glyph_By_ID( MVF_Face *   face,
                 U16   glyph_id,
                 MVF_Outline * outline) //[OUT]
{
    U16 i, flags;
    S32 min_x, min_y;
    U8 * pos;

    D_ASSERT( face != 0 );
    D_ASSERT( outline != 0 );
    D_ASSERT( face->glyphs != 0 );

    pos = face->glyphs[glyph_id].outline_data;

    D_ASSERT( pos != 0 );

    if ( glyph_id >= face->num_glyphs )
        return -6; //no such ID

    /*outline->width = MVF_PEEK_USHORT(pos);
    MVF_NEXT_USHORT(pos);
    outline->height = MVF_PEEK_USHORT(pos);
    MVF_NEXT_USHORT(pos);
    outline->x_shift = MVF_PEEK_SHORT(pos);
    MVF_NEXT_SHORT(pos);
    outline->y_shift = MVF_PEEK_SHORT(pos);
    MVF_NEXT_SHORT(pos);*/

    outline->n_points = MVF_PEEK_USHORT(pos);
    MVF_NEXT_USHORT(pos);

    if ( outline->n_points == 0 )
    {
        outline->points = 0;
        outline->tags = 0;
        outline->n_contours = 0;
    }
    else
    {
        flags = MVF_PEEK_BYTE(pos); //use16
        MVF_NEXT_BYTE(pos);
        min_x = MVF_PEEK_LONG(pos);
        MVF_NEXT_LONG(pos);
        min_y = MVF_PEEK_LONG(pos);
        MVF_NEXT_LONG(pos);

        outline->points = (MVF_Vector*) MVF_MALLOC(sizeof(MVF_Vector)*outline->n_points);
        if(outline->points == NULL)
        {
		outline->n_contours = 0;
		outline->contours = NULL;
		return -1;
        }

        if (flags & MVF_OUTLINE_X_USE16) //X
            for (i = 0; i < outline->n_points; i++)
            {
                outline->points[i].x = (MVF_Pos)MVF_PEEK_USHORT(pos) + min_x;
                MVF_NEXT_USHORT(pos);
            }
        else //use 24bits
            for (i = 0; i < outline->n_points; i++)
            {
                outline->points[i].x = (MVF_Pos)MVF_PEEK_U24(pos) + min_x;
                MVF_NEXT_U24(pos);
            }
        if (flags & MVF_OUTLINE_Y_USE16) //Y
            for (i = 0; i < outline->n_points; i++)
            {
                outline->points[i].y = (MVF_Pos)MVF_PEEK_USHORT(pos) + min_y;
                MVF_NEXT_USHORT(pos);
            }
        else //use 24bits
            for (i = 0; i < outline->n_points; i++)
            {
                outline->points[i].y = (MVF_Pos)MVF_PEEK_U24(pos) + min_y;
                MVF_NEXT_U24(pos);
            }

        outline->tags = pos;
        pos += outline->n_points * (sizeof(U8)); //tags

        if ( outline->n_points < 256 ) //can present in a byte...
        {
            outline->n_contours = MVF_PEEK_BYTE(pos);
            MVF_NEXT_BYTE(pos);
        }
        else
        {
            outline->n_contours = MVF_PEEK_USHORT(pos);
            MVF_NEXT_USHORT(pos);
        }
    }

    if ( outline->n_contours == 0 )
    {
        outline->contours = 0;
    }
    else
    {

        outline->contours = (U16*) MVF_MALLOC(sizeof(S16)*outline->n_contours);
        if(outline->contours == NULL)
        {
              if(outline->points)
              {
			 MVF_FREE(outline->points);
			 outline->points = NULL;
              }
		outline->n_points = 0;
		outline->n_contours = 0;
		return -1;
        }
        if ( outline->n_points < 256 ) //can present in a byte...
        {
            for (i = 0; i < outline->n_contours; i++)
            {
                outline->contours[i] = MVF_PEEK_BYTE(pos);
                MVF_NEXT_BYTE(pos);
            }
        }
        else
        {
            for (i = 0; i < outline->n_contours; i++)
            {
                outline->contours[i] = MVF_PEEK_USHORT(pos);
                MVF_NEXT_USHORT(pos);
            }
        }
    }


#if MVF_HINTING_ENABLE

    if (outline->n_points == 0)
    {
        outline->hinting_n_touch_points = 0;
        outline->hinting_n_sizes = 0;
        outline->hinting_sizes = 0;
        outline->hinting_data = 0;
    }
    else
    {
        outline->hinting_n_sizes = MVF_PEEK_BYTE(pos);
        MVF_NEXT_BYTE(pos);
        outline->hinting_sizes = pos; //list of size...
        pos += sizeof(U8)*outline->hinting_n_sizes;
        outline->hinting_adv_x = pos; //list of advx...
        pos += sizeof(U8)*outline->hinting_n_sizes;
        outline->hinting_n_touch_points = MVF_PEEK_USHORT(pos);
        MVF_NEXT_USHORT(pos);
        outline->hinting_data = pos; //touch points....
        pos += sizeof(S16)*outline->hinting_n_touch_points*outline->hinting_n_sizes;
    }

#endif

    outline->advance_x = MVF_PEEK_USHORT(pos);
    MVF_NEXT_USHORT(pos);
    outline->metrics_height = MVF_PEEK_USHORT(pos);
    MVF_NEXT_USHORT(pos);
    outline->ascender = MVF_PEEK_SHORT(pos);
    MVF_NEXT_SHORT(pos);
    outline->descender = MVF_PEEK_SHORT(pos);
    MVF_NEXT_SHORT(pos);

    return 0;
}

void MVF_Done_Outline(
                 MVF_Outline * outline)
{
    D_ASSERT( outline != 0 );

    if (outline->contours)
        MVF_FREE(outline->contours);
    //if (face->tags)
     //   MVF_FREE(face->tags);
    if (outline->points)
        MVF_FREE(outline->points);
    MVF_MEM_ZERO(outline, sizeof(MVF_Outline));
}


#undef _MAPI_GFX2D_C
