/**
 * @file hv_drv_OsdFont.c
 * @brief Osd Driver font drawing c file.
 * @details This file provides the following functions: \n
 *          (1) font initialization functions \n
 *          (2) menu window font draw functions \n
 *          (3) menu window font update functions \n
 *          (4) menu window font hide functions \n
 *          (5) info window font draw functions \n
 *
 * @author HiView SoC Software Team
 * @version 1.0.0
 * @date 2022-08-31
 * @copyright Copyright(c),2022-8, Hiview Software. All rights reserved.
 * @par History:
 * <table>
 * <tr><th>Author         <th>Date        <th>Change Description
 * <tr><td>OSD Group      <td>2022-08-31   <td>create
 * </table>
*/

#include "hv_drv_Osd.h"

//#define MARQUEE_TEST
//#define CPUCANVAS_TEST

static OsdFontParam *s_pstFontParam = NULL;

#ifdef CPUCANVAS_TEST
OsdComponentNode *s_pstCPUCanvasNode = NULL;
#endif

#ifdef HV_SCALER_DEBUG_VERSION
USHORT16 g_usTotalComponentWidth = 0;
USHORT16 g_usMaxComponentWidth = 0;

static VOID Hv_Drv_OsdDebug_SubComponentWidth(OsdComponentNode *pstCur)
{
    g_usTotalComponentWidth -= pstCur->stBox.usW;
}
#endif

OsdPhyFontInfo* Hv_Drv_OsdFont_GetCurrentFontInfo(VOID)
{
    OsdWindowPhyId enCurrentWinIndex = Hv_Cal_OsdWindow_GetCurrentWindow();

    return &s_pstFontParam->pstOsdPhyFonts[enCurrentWinIndex];
}

static VOID* Hv_Drv_OsdFont_GetComponentData(OsdComponentNode *pstNode)
{
    return pstNode->pvDynamicData ? pstNode->pvDynamicData : pstNode->pvCpData;
}

static BOOL Hv_Drv_OsdFont_ComponentIsBmpOrIcon(OsdComponentNode *pstNode)
{
    OsdComponent stCp = pstNode->stCp;

    if ((stCp.enType == OSD_CP_TYPE_BMP) || (stCp.enType == OSD_CP_TYPE_BMP_LIST) ||
            (stCp.enType == OSD_CP_TYPE_ICON) || (stCp.enType == OSD_CP_TYPE_ICON_LIST))
    {
        return HV_TRUE;
    }

    return HV_FALSE;
}

static USHORT16 Hv_Drv_OsdFont_GetComponentX(OsdComponentNode *pstNode)
{
    UINT32 uiAddr = 0;

    static const USHORT16 s_ausComponentXOffsets[] = {
        /* OSD_CP_TYPE_NONE */
        0,
        /* OSD_CP_TYPE_TEXT */
        offsetof(OsdText, usX),
        /* OSD_CP_TYPE_TEXT_LIST */
        offsetof(OsdTextList, usX),
        /* OSD_CP_TYPE_NUMBER */
        offsetof(OsdNumber, usX),
        /* OSD_CP_TYPE_INTACT_NUMBER */
        0, // TODO
        /* OSD_CP_TYPE_ASCII_STRING */
        offsetof(OsdAsciiString, usX),
        /* OSD_CP_TYPE_ICON */
        offsetof(OsdIcon, usX),
        /* OSD_CP_TYPE_ICON_LIST */
        offsetof(OsdIconList, usX),
        /* OSD_CP_TYPE_BMP */
        offsetof(OsdBmp, usX),
        /* OSD_CP_TYPE_BMP_LIST */
        offsetof(OsdBmpList, usX),
#if (HV_PROJECT_CONFIG_OSD_CUSTOM_GROUP_SUPPORT == HV_CONFIG_ON)
        /* OSD_CP_TYPE_CUSTOM_GROUP */
        offsetof(OsdCustomGroupInfo, usX),
#endif
    };

    uiAddr = (UINT32)Hv_Drv_OsdFont_GetComponentData(pstNode);

    return *(USHORT16*)(uiAddr + s_ausComponentXOffsets[pstNode->stCp.enType]);
}

static VOID Hv_Drv_OsdFont_SetComponentX(OsdComponentNode *pstNode, USHORT16 usX)
{
    UINT32 uiAddr = 0;

    static const USHORT16 s_ausComponentXOffsets[] = {
        /* OSD_CP_TYPE_NONE */
        0,
        /* OSD_CP_TYPE_TEXT */
        offsetof(OsdText, usX),
        /* OSD_CP_TYPE_TEXT_LIST */
        offsetof(OsdTextList, usX),
        /* OSD_CP_TYPE_NUMBER */
        offsetof(OsdNumber, usX),
        /* OSD_CP_TYPE_INTACT_NUMBER */
        0, // TODO
        /* OSD_CP_TYPE_ASCII_STRING */
        offsetof(OsdAsciiString, usX),
        /* OSD_CP_TYPE_ICON */
        offsetof(OsdIcon, usX),
        /* OSD_CP_TYPE_ICON_LIST */
        offsetof(OsdIconList, usX),
        /* OSD_CP_TYPE_BMP */
        offsetof(OsdBmp, usX),
        /* OSD_CP_TYPE_BMP_LIST */
        offsetof(OsdBmpList, usX),
#if (HV_PROJECT_CONFIG_OSD_CUSTOM_GROUP_SUPPORT == HV_CONFIG_ON)
        /* OSD_CP_TYPE_CUSTOM_GROUP */
        offsetof(OsdCustomGroupInfo, usX),
#endif
    };

    uiAddr = (UINT32)Hv_Drv_OsdFont_GetComponentData(pstNode);

    *(USHORT16*)(uiAddr + s_ausComponentXOffsets[pstNode->stCp.enType]) = usX;

    return;
}

USHORT16 Hv_Drv_OsdFont_GetComponentY(OsdComponentNode *pstNode)
{
    UINT32 uiAddr = 0;

    static const USHORT16 s_ausComponentYOffsets[] = {
        /* OSD_CP_TYPE_NONE */
        0,
        /* OSD_CP_TYPE_TEXT */
        offsetof(OsdText, usY),
        /* OSD_CP_TYPE_TEXT_LIST */
        offsetof(OsdTextList, usY),
        /* OSD_CP_TYPE_NUMBER */
        offsetof(OsdNumber, usY),
        /* OSD_CP_TYPE_INTACT_NUMBER */
        0, // TODO
        /* OSD_CP_TYPE_ASCII_STRING */
        offsetof(OsdAsciiString, usY),
        /* OSD_CP_TYPE_ICON */
        offsetof(OsdIcon, usY),
        /* OSD_CP_TYPE_ICON_LIST */
        offsetof(OsdIconList, usY),
        /* OSD_CP_TYPE_BMP */
        offsetof(OsdBmp, usY),
        /* OSD_CP_TYPE_BMP_LIST */
        offsetof(OsdBmpList, usY),
#if (HV_PROJECT_CONFIG_OSD_CUSTOM_GROUP_SUPPORT == HV_CONFIG_ON)
        /* OSD_CP_TYPE_CUSTOM_GROUP */
        offsetof(OsdCustomGroupInfo, usY),
#endif
    };

    uiAddr = (UINT32)Hv_Drv_OsdFont_GetComponentData(pstNode);

    return *(USHORT16*)(uiAddr + s_ausComponentYOffsets[pstNode->stCp.enType]);
}

VOID Hv_Drv_OsdFont_SetComponentY(OsdComponentNode *pstNode, USHORT16 usY)
{
    UINT32 uiAddr = 0;

    static const USHORT16 s_ausComponentYOffsets[] = {
        /* OSD_CP_TYPE_NONE */
        0,
        /* OSD_CP_TYPE_TEXT */
        offsetof(OsdText, usY),
        /* OSD_CP_TYPE_TEXT_LIST */
        offsetof(OsdTextList, usY),
        /* OSD_CP_TYPE_NUMBER */
        offsetof(OsdNumber, usY),
        /* OSD_CP_TYPE_INTACT_NUMBER */
        0, // TODO
        /* OSD_CP_TYPE_ASCII_STRING */
        offsetof(OsdAsciiString, usY),
        /* OSD_CP_TYPE_ICON */
        offsetof(OsdIcon, usY),
        /* OSD_CP_TYPE_ICON_LIST */
        offsetof(OsdIconList, usY),
        /* OSD_CP_TYPE_BMP */
        offsetof(OsdBmp, usY),
        /* OSD_CP_TYPE_BMP_LIST */
        offsetof(OsdBmpList, usY),
#if (HV_PROJECT_CONFIG_OSD_CUSTOM_GROUP_SUPPORT == HV_CONFIG_ON)
        /* OSD_CP_TYPE_CUSTOM_GROUP */
        offsetof(OsdCustomGroupInfo, usY),
#endif
    };

    uiAddr = (UINT32)Hv_Drv_OsdFont_GetComponentData(pstNode);

    *(USHORT16*)(uiAddr + s_ausComponentYOffsets[pstNode->stCp.enType]) = usY;
}
#if (HV_PROJECT_CONFIG_OSD_MARQUEE == HV_CONFIG_ON)
OsdMarqueeType Hv_Drv_OsdFont_GetMarqueeType(OsdComponentNode *pstNode)
{
    if (OSD_MARQUEE_TYPE_DEFAULT == pstNode->enMarqueeType)
    {
        return HV_OSD_CONFIG_DEFAULT_MARQUEE_TYPE;
    }

    return pstNode->enMarqueeType;
}

BOOL Hv_Drv_OsdFont_GetMarqueeEnable(OsdComponentNode *pstNode)
{
    return pstNode->enMarqueeType != OSD_MARQUEE_TYPE_NONE;
}

VOID Hv_Drv_OsdFont_SetMarqueeType(OsdComponentNode *pstNode, OsdMarqueeType enMarqueeType)
{
    OSD_DRV_LOG_DEBUG("set component %d-%d marquee type to %d", pstNode->stCp.enType, pstNode->stCp.usIndex, enMarqueeType);

    pstNode->enMarqueeType = enMarqueeType;
    if (OSD_MARQUEE_TYPE_DEFAULT == enMarqueeType)
    {
        /* Auto start marquee for excessed component */
        pstNode->bStartAfterCreate = HV_TRUE;
    }
}
#endif

#if (HV_PROJECT_CONFIG_OSD_CPU_CANVAS_SUPPORT == HV_CONFIG_ON)
BOOL Hv_Drv_OsdFont_IsCPUCanvas(OsdComponentNode *pstNode)
{
    UINT32 uiAddr = 0;
    OsdComponent stCp = pstNode->stCp;
    OsdIcon *pstIcon = NULL;
    OsdBmp *pstBmp = NULL;

    uiAddr = (UINT32)Hv_Drv_OsdFont_GetComponentData(pstNode);

    if (stCp.enType != OSD_CP_TYPE_BMP && stCp.enType != OSD_CP_TYPE_ICON)
    {
        return HV_FALSE;
    }

    if (stCp.enType == OSD_CP_TYPE_ICON)
    {
        pstIcon = (OsdIcon *)uiAddr;

        return pstIcon->uiIsCPUCanvas;
    }
    else
    {
        pstBmp = (OsdBmp *)uiAddr;

        return pstBmp->uiIsCPUCanvas;
    }
}
#endif

static UCHAR8 Hv_Drv_OsdFont_GetComponentWindowIndex(OsdComponentNode *pstNode)
{
    UINT32 uiAddr = 0;

    static const USHORT16 s_ausComponentWindowIndexOffsets[] = {
        /* OSD_CP_TYPE_NONE */
        0,
        /* OSD_CP_TYPE_TEXT */
        offsetof(OsdText, ucWindowIndex),
        /* OSD_CP_TYPE_TEXT_LIST */
        offsetof(OsdTextList, ucWindowIndex),
        /* OSD_CP_TYPE_NUMBER */
        offsetof(OsdNumber, ucWindowIndex),
        /* OSD_CP_TYPE_INTACT_NUMBER */
        0, // TODO
        /* OSD_CP_TYPE_ASCII_STRING */
        offsetof(OsdAsciiString, ucWindowIndex),
        /* OSD_CP_TYPE_ICON */
        offsetof(OsdIcon, ucWindowIndex),
        /* OSD_CP_TYPE_ICON_LIST */
        offsetof(OsdIconList, ucWindowIndex),
        /* OSD_CP_TYPE_BMP */
        offsetof(OsdBmp, ucWindowIndex),
        /* OSD_CP_TYPE_BMP_LIST */
        offsetof(OsdBmpList, ucWindowIndex),
    };

    uiAddr = (UINT32)Hv_Drv_OsdFont_GetComponentData(pstNode);

    return *(UCHAR8*)(uiAddr + s_ausComponentWindowIndexOffsets[pstNode->stCp.enType]);
}

UCHAR8 Hv_Drv_OsdFont_GetComponentColorIndex(OsdComponentNode *pstNode)
{
    UINT32 uiAddr = 0;

    static const USHORT16 s_ausComponentColorIndexOffsets[] = {
        /* OSD_CP_TYPE_NONE */
        0,
        /* OSD_CP_TYPE_TEXT */
        offsetof(OsdText, ucColorIndex),
        /* OSD_CP_TYPE_TEXT_LIST */
        offsetof(OsdTextList, ucColorIndex),
        /* OSD_CP_TYPE_NUMBER */
        offsetof(OsdNumber, ucColorIndex),
        /* OSD_CP_TYPE_INTACT_NUMBER */
        0, // TODO
        /* OSD_CP_TYPE_ASCII_STRING */
        offsetof(OsdAsciiString, ucColorIndex),
        /* OSD_CP_TYPE_ICON */
        offsetof(OsdIcon, ucColorIndex),
        /* OSD_CP_TYPE_ICON_LIST */
        offsetof(OsdIconList, ucColorIndex),
        /* OSD_CP_TYPE_BMP */
        0,
       /* OSD_CP_TYPE_BMP_LIST */
        0,
    };

    uiAddr = (UINT32)Hv_Drv_OsdFont_GetComponentData(pstNode);

    return *(UCHAR8*)(uiAddr + s_ausComponentColorIndexOffsets[pstNode->stCp.enType]);
}

USHORT16 Hv_Drv_OsdFont_GetComponentResH(OsdComponentNode *pstNode)
{
    UINT32 uiAddr = 0;
    UCHAR8 ucBlockId = 0;
    UCHAR8 ucWidth = 0;
    UCHAR8 ucHeight = 0;

    static const USHORT16 s_ausComponentBlockIdOffsets[] = {
        /* OSD_CP_TYPE_NONE */
        0,
        /* OSD_CP_TYPE_TEXT */
        offsetof(OsdText, ucBlockID),
        /* OSD_CP_TYPE_TEXT_LIST */
        offsetof(OsdTextList, ucBlockID),
        /* OSD_CP_TYPE_NUMBER */
        offsetof(OsdNumber, ucBlockID),
        /* OSD_CP_TYPE_INTACT_NUMBER */
        0, // TODO
        /* OSD_CP_TYPE_ASCII_STRING */
        offsetof(OsdAsciiString, ucBlockID),
        /* OSD_CP_TYPE_ICON */
        offsetof(OsdIcon, ucBlockID),
        /* OSD_CP_TYPE_ICON_LIST */
        offsetof(OsdIconList, ucBlockID),
        /* OSD_CP_TYPE_BMP */
        offsetof(OsdBmp, ucBlockID),
       /* OSD_CP_TYPE_BMP_LIST */
        offsetof(OsdBmpList, ucBlockID),
    };

    uiAddr = (UINT32)Hv_Drv_OsdFont_GetComponentData(pstNode);
    ucBlockId = *(UCHAR8 *)(uiAddr + s_ausComponentBlockIdOffsets[pstNode->stCp.enType]);
    HV_ASSERT_TRUE_RET(ucBlockId < OSD_BLOCK_COUNT, 0);
    ucWidth = 0;
    ucHeight = 0;

    Hv_Drv_OsdResource_GetSize(g_astAllBlocks[ucBlockId].usResourceSize, &ucWidth, &ucHeight);
    return ucHeight;
}

VOID Hv_Drv_OsdFont_GetComponentResType(OsdComponentNode *pstNode, USHORT16 *pusResourceSize, UCHAR8 *pucBitType)
{
    HV_ASSERT_VALID_PTR_VOID(pstNode);
    HV_ASSERT_VALID_PTR_VOID(pusResourceSize);
    HV_ASSERT_VALID_PTR_VOID(pucBitType);

    UINT32 uiAddr = 0;
    UCHAR8 ucBlockId = 0;
    UCHAR8 ucWidth = 0;
    UCHAR8 ucHeight = 0;

    static const USHORT16 s_ausComponentBlockIdOffsets[] = {
        /* OSD_CP_TYPE_NONE */
        0,
        /* OSD_CP_TYPE_TEXT */
        offsetof(OsdText, ucBlockID),
        /* OSD_CP_TYPE_TEXT_LIST */
        offsetof(OsdTextList, ucBlockID),
        /* OSD_CP_TYPE_NUMBER */
        offsetof(OsdNumber, ucBlockID),
        /* OSD_CP_TYPE_INTACT_NUMBER */
        0, // TODO
        /* OSD_CP_TYPE_ASCII_STRING */
        offsetof(OsdAsciiString, ucBlockID),
        /* OSD_CP_TYPE_ICON */
        offsetof(OsdIcon, ucBlockID),
        /* OSD_CP_TYPE_ICON_LIST */
        offsetof(OsdIconList, ucBlockID),
        /* OSD_CP_TYPE_BMP */
        offsetof(OsdBmp, ucBlockID),
       /* OSD_CP_TYPE_BMP_LIST */
        offsetof(OsdBmpList, ucBlockID),
    };

    uiAddr = (UINT32)Hv_Drv_OsdFont_GetComponentData(pstNode);
    ucBlockId = *(UCHAR8 *)(uiAddr + s_ausComponentBlockIdOffsets[pstNode->stCp.enType]);
    HV_ASSERT_TRUE_VOID(ucBlockId < OSD_BLOCK_COUNT);

    *pusResourceSize = g_astAllBlocks[ucBlockId].usResourceSize;
    *pucBitType = g_astAllBlocks[ucBlockId].ucResourceType;
}

static USHORT16 Hv_Drv_OsdFont_GetComponentWidth(OsdComponentNode *pstNode)
{
    UINT32 uiAddr = 0;
    HV_ASSERT_VALID_PTR_RET(pstNode, 0);
    uiAddr = (UINT32)Hv_Drv_OsdFont_GetComponentData(pstNode);
    HV_ASSERT_TRUE_RET(uiAddr != 0, 0);
    switch (pstNode->stCp.enType)
    {
        case OSD_CP_TYPE_TEXT:
        {
            return ((OsdText *)uiAddr)->usWidth;
        }
        case OSD_CP_TYPE_TEXT_LIST:
        {
            return ((OsdTextList *)uiAddr)->usWidth;
        }
        case OSD_CP_TYPE_NUMBER:
        {
            return ((OsdNumber *)uiAddr)->usWidth;
        }
        case OSD_CP_TYPE_ASCII_STRING:
        {
            return ((OsdAsciiString *)uiAddr)->usWidth;
        }
#if (HV_PROJECT_CONFIG_OSD_CUSTOM_GROUP_SUPPORT == HV_CONFIG_ON)
        case OSD_CP_TYPE_CUSTOM_GROUP:
        {
            return ((OsdCustomGroupInfo*)uiAddr)->usWidth;
        }
#endif
        default:
        {
            return 0;
        }
    }
}

static const OsdBlock* Hv_Drv_OsdFont_GetBlock(OsdComponentNode *pstNode)
{
    UINT32 uiAddr = 0;
    UCHAR8 ucBlockID = 0;

    static const USHORT16 s_ausComponentBlockIDOffsets[] = {
        /* OSD_CP_TYPE_NONE */
        0,
        /* OSD_CP_TYPE_TEXT */
        offsetof(OsdText, ucBlockID),
        /* OSD_CP_TYPE_TEXT_LIST */
        offsetof(OsdTextList, ucBlockID),
        /* OSD_CP_TYPE_NUMBER */
        offsetof(OsdNumber, ucBlockID),
        /* OSD_CP_TYPE_INTACT_NUMBER */
        0, // TODO
        /* OSD_CP_TYPE_ASCII_STRING */
        offsetof(OsdAsciiString, ucBlockID),
        /* OSD_CP_TYPE_ICON */
        offsetof(OsdIcon, ucBlockID),
        /* OSD_CP_TYPE_ICON_LIST */
        offsetof(OsdIconList, ucBlockID),
        /* OSD_CP_TYPE_BMP */
        offsetof(OsdBmp, ucBlockID),
        /* OSD_CP_TYPE_BMP_LIST */
        offsetof(OsdBmpList, ucBlockID),
    };

    uiAddr = (UINT32)Hv_Drv_OsdFont_GetComponentData(pstNode);

    ucBlockID = *(UCHAR8*)(uiAddr + s_ausComponentBlockIDOffsets[pstNode->stCp.enType]);
    OSD_DRV_LOG_DEBUG("block id of component: %d", ucBlockID);

    return &g_astAllBlocks[ucBlockID];
}

OsdResourceType Hv_Drv_OsdFont_GetComponentResourceType(OsdComponentNode *pstNode)
{
    const OsdBlock *pstBlock = NULL;

    pstBlock = Hv_Drv_OsdFont_GetBlock(pstNode);
    return pstBlock->ucResourceType;
}

#if (HV_PROJECT_CONFIG_OSD_BIGFONT_SUPPORT == HV_CONFIG_ON)

static const OsdIcon* Hv_Drv_OsdFont_SearchBigFontIcon(USHORT16 usCode)
{
    USHORT16 usIndex = 0;
    USHORT16 usArrayLen = g_usBigIconFontArrayLen;
    const OsdIcon *pstIcon = NULL;

    for (usIndex = 0; usIndex < usArrayLen; ++usIndex)
    {
        pstIcon = &g_astIconPropBigFontArray[usIndex];
        if (usCode == pstIcon->usIconCount)
        {
            return pstIcon;
        }
    }

    return NULL;
}

static const OsdBmp* Hv_Drv_OsdFont_SearchBigFontBmp(USHORT16 usCode)
{
    USHORT16 usIndex = 0;
    USHORT16 usArrayLen = g_usBigBmpFontArrayLen;
    const OsdBmp *pstBmp = NULL;

    for (usIndex = 0; usIndex < usArrayLen; ++usIndex)
    {
        pstBmp = &g_astBmpPropBigFontArray[usIndex];
        if (usCode == pstBmp->usBmpCount)
        {
            return pstBmp;
        }
    }

    return NULL;
}

UCHAR8 Hv_Drv_OsdFont_GetBigFontComponentRows(OsdAsciiString *pstAscii)
{
    const OsdIcon *pstIcon = NULL;
    const OsdBmp *pstBmp = NULL;

    if (pstAscii->usIsBigIconFont)
    {
        pstIcon = Hv_Drv_OsdFont_SearchBigFontIcon(pstAscii->ausString[0]);
        HV_ASSERT_TRUE_RET(NULL != pstIcon, 1);

        return pstIcon->ucVCount;
    }
    else
    {
        pstBmp = Hv_Drv_OsdFont_SearchBigFontBmp(pstAscii->ausString[0]);
        HV_ASSERT_TRUE_RET(NULL != pstBmp, 1);

        return pstBmp->ucVCount;
    }
}
#endif

UCHAR8 Hv_Drv_OsdFont_GetComponentRows(OsdComponentNode *pstNode)
{
    OsdText *pstText = NULL;
    OsdAsciiString *pstAscii = NULL;
    OsdIconList *pstIconList = NULL;
    OsdBmpList *pstBmpList = NULL;
    OsdIcon *pstIcon = NULL;
    OsdBmp *pstBmp = NULL;

    switch (pstNode->stCp.enType)
    {
        case OSD_CP_TYPE_TEXT:
        {
            pstText = (OsdText*)(Hv_Drv_OsdFont_GetComponentData(pstNode));
#if (HV_PROJECT_CONFIG_OSD_TEXT_WRAPPING == HV_CONFIG_ON)
            if (pstText->usIsLongText)
            {
                return pstText->ucWrapMaxRows;
            }
#endif
            return 1;
        }
        case OSD_CP_TYPE_ASCII_STRING:
        {
            pstAscii = (OsdAsciiString*)(Hv_Drv_OsdFont_GetComponentData(pstNode));
#if (HV_PROJECT_CONFIG_OSD_TEXT_WRAPPING == HV_CONFIG_ON)
            if (pstAscii->usIsLongText)
            {
                return pstAscii->ucWrapMaxRows;
            }
#endif
#if (HV_PROJECT_CONFIG_OSD_BIGFONT_SUPPORT == HV_CONFIG_ON)
            if (pstAscii->usIsBigIconFont || pstAscii->usIsBigBmpFont)
            {
                return Hv_Drv_OsdFont_GetBigFontComponentRows(pstAscii);
            }
#endif
            return 1;
        }
        case OSD_CP_TYPE_ICON:
        {
            pstIcon = (OsdIcon*)(Hv_Drv_OsdFont_GetComponentData(pstNode));
            return pstIcon->ucVCount;
        }
        case OSD_CP_TYPE_BMP:
        {
            pstBmp = (OsdBmp*)(Hv_Drv_OsdFont_GetComponentData(pstNode));
            return pstBmp->ucVCount;
        }
        case OSD_CP_TYPE_ICON_LIST:
        {
            pstIconList = (OsdIconList*)(Hv_Drv_OsdFont_GetComponentData(pstNode));
            return pstIconList->ucVCount;
        }
        case OSD_CP_TYPE_BMP_LIST:
        {
            pstBmpList = (OsdBmpList*)(Hv_Drv_OsdFont_GetComponentData(pstNode));
            return pstBmpList->ucVCount;
        }
        default:
        {
            break;
        }
    }

    return 1;
}

UCHAR8 Hv_Drv_OsdFont_GetComponentColumns(OsdComponentNode *pstNode)
{
    UCHAR8 ucWidth = 0;
    const OsdBlock *pstBlock = NULL;
    OsdIconList *pstIconList = NULL;
    OsdBmpList *pstBmpList = NULL;
    OsdIcon *pstIcon = NULL;
    OsdBmp *pstBmp = NULL;

    pstBlock = Hv_Drv_OsdFont_GetBlock(pstNode);

    switch (pstNode->stCp.enType)
    {
        case OSD_CP_TYPE_ICON:
        {
            pstIcon = (OsdIcon*)(Hv_Drv_OsdFont_GetComponentData(pstNode));
            return pstIcon->ucHCount;
        }
        case OSD_CP_TYPE_BMP:
        {
            pstBmp = (OsdBmp*)(Hv_Drv_OsdFont_GetComponentData(pstNode));
            return pstBmp->ucHCount;
        }
        case OSD_CP_TYPE_ICON_LIST:
        {
            pstIconList = (OsdIconList*)(Hv_Drv_OsdFont_GetComponentData(pstNode));
            return pstIconList->ucHCount;
        }
        case OSD_CP_TYPE_BMP_LIST:
        {
            pstBmpList = (OsdBmpList*)(Hv_Drv_OsdFont_GetComponentData(pstNode));
            return pstBmpList->ucHCount;
        }
        default:
        {
            Hv_Drv_OsdResource_GetSize(pstBlock->usResourceSize, &ucWidth, NULL);

            return roundup_div(pstNode->stBox.usW, ucWidth);
        }
    }

    return 1;
}

BOOL IterTextGetCurrentCharacter(OsdComponentIterator *pstIterator, const UCHAR8 **ppMatrixAddr,
                                    USHORT16 *pusOffset, UCHAR8 *pucRealWidth)
{
    OsdText *pstText = NULL;
    USHORT16 usGlobalFontCount = 0;
    const UCHAR8 *pucWidth = NULL;
    const UCHAR8 *pucGlobalWidth = NULL;
    const UCHAR8 *pucMatrixAddr = NULL;
    const OsdBlock *pstBlock = NULL;
    const OsdBlock *pstGlobalBlock = NULL;
    OsdTextIterator *pstParam = (OsdTextIterator*)&pstIterator->pvArgs;
    USHORT16 *pusString = pstParam->pusString;

    pstText = (OsdText*)Hv_Drv_OsdFont_GetComponentData(pstIterator->pstComponent);

    pstBlock = &g_astAllBlocks[pstText->ucBlockID];
    pucWidth = Hv_Drv_OsdResource_GetRealWidth(pstBlock);

    usGlobalFontCount = pstBlock->usGlobalFontCount;
    if (usGlobalFontCount > 0)
    {
        pstGlobalBlock = &g_astAllBlocks[pstBlock->ucGlobalBlockID];
        pucGlobalWidth = Hv_Drv_OsdResource_GetRealWidth(pstGlobalBlock);
    }

    if (*pusString < usGlobalFontCount)
    {
        *ppMatrixAddr = Hv_Drv_OsdResource_GetBlockMatrixAddr(pstGlobalBlock);
        *pusOffset = *pusString;
        *pucRealWidth = pucGlobalWidth[*pusString];

        OSD_DRV_LOG_DEBUG("Text Get current global text: 0x%x offset %d realwidth %d", pusString, *pusOffset, *pucRealWidth);
    }
    else
    {
        *ppMatrixAddr = Hv_Drv_OsdResource_GetBlockMatrixAddr(pstBlock);
        *pusOffset = *pusString - usGlobalFontCount;
        *pucRealWidth = pucWidth[*pusString - usGlobalFontCount];

        OSD_DRV_LOG_DEBUG("Text Get current non-global text: 0x%x offset %d realwidth %d", pusString, *pusOffset, *pucRealWidth);
    }

    return HV_TRUE;
}

VOID IterTextStepForward(OsdComponentIterator *pstIterator)
{
    OsdTextIterator *pstParam = (OsdTextIterator*)&pstIterator->pvArgs;

    ++pstParam->pusString;
}

BOOL IterTextIsEnd(OsdComponentIterator *pstIterator)
{
    OsdTextIterator *pstParam = (OsdTextIterator*)&pstIterator->pvArgs;

    return *(pstParam->pusString) == __END__;
}

VOID IterTextReset(OsdComponentIterator *pstIterator)
{
    OsdText *pstText = NULL;
    OsdLanguage enCurrentLanguage = 0;
    OsdTextIterator *pstParam = (OsdTextIterator*)&pstIterator->pvArgs;

    pstText = (OsdText*)Hv_Drv_OsdFont_GetComponentData(pstIterator->pstComponent);

    enCurrentLanguage = Hv_Drv_OsdFrame_GetCurrentLanguage();
    pstParam->pusString = g_apOsdStringList[pstText->usStringId][enCurrentLanguage];

    OSD_DRV_LOG_DEBUG("Iterator text reset to begining 0x%x", pstParam->pusString);
}

BOOL IterTextListGetCurrentCharacter(OsdComponentIterator *pstIterator, const UCHAR8 **ppMatrixAddr,
                                    USHORT16 *pusOffset, UCHAR8 *pucRealWidth)
{
    OsdTextList *pstTextList = NULL;
    USHORT16 usGlobalFontCount = 0;
    const UCHAR8 *pucWidth = NULL;
    const UCHAR8 *pucGlobalWidth = NULL;
    const UCHAR8 *pucMatrixAddr = NULL;
    const OsdBlock *pstBlock = NULL;
    const OsdBlock *pstGlobalBlock = NULL;
    OsdTextIterator *pstParam = (OsdTextIterator*)&pstIterator->pvArgs;
    USHORT16 *pusString = pstParam->pusString;

    pstTextList = (OsdTextList*)Hv_Drv_OsdFont_GetComponentData(pstIterator->pstComponent);

    pstBlock = &g_astAllBlocks[pstTextList->ucBlockID];
    pucWidth = Hv_Drv_OsdResource_GetRealWidth(pstBlock);

    usGlobalFontCount = pstBlock->usGlobalFontCount;
    if (usGlobalFontCount > 0)
    {
        pstGlobalBlock = &g_astAllBlocks[pstBlock->ucGlobalBlockID];
        pucGlobalWidth = Hv_Drv_OsdResource_GetRealWidth(pstGlobalBlock);
    }

    if (*pusString < usGlobalFontCount)
    {
        *ppMatrixAddr = Hv_Drv_OsdResource_GetBlockMatrixAddr(pstGlobalBlock);
        *pusOffset = *pusString;
        *pucRealWidth = pucGlobalWidth[*pusString];
    }
    else
    {
        *ppMatrixAddr = Hv_Drv_OsdResource_GetBlockMatrixAddr(pstBlock);
        *pusOffset = *pusString - usGlobalFontCount;
        *pucRealWidth = pucWidth[*pusString - usGlobalFontCount];
    }

    return HV_TRUE;
}

VOID IterTextListReset(OsdComponentIterator *pstIterator)
{
    OsdTextList *pstTextList = NULL;
    OsdLanguage enCurrentLanguage = 0;
    OsdTextIterator *pstParam = (OsdTextIterator*)&pstIterator->pvArgs;

    pstTextList = (OsdTextList*)Hv_Drv_OsdFont_GetComponentData(pstIterator->pstComponent);

    enCurrentLanguage = Hv_Drv_OsdFrame_GetCurrentLanguage();
    pstParam->pusString =
        g_apOsdStringList[pstTextList->pusStringIDList[pstTextList->ucStringCurID]][enCurrentLanguage];
}

BOOL IterAsciiGetCurrentCharacter(OsdComponentIterator *pstIterator, const UCHAR8 **ppMatrixAddr,
                                    USHORT16 *pusOffset, UCHAR8 *pucRealWidth)
{
    OsdAsciiString *pstAscii = NULL;
    const UCHAR8 *pucWidth = NULL;
    const OsdBlock *pstBlock = NULL;
    OsdAsciiIterator *pstParam = (OsdAsciiIterator*)&pstIterator->pvArgs;

    pstAscii = (OsdAsciiString*)Hv_Drv_OsdFont_GetComponentData(pstIterator->pstComponent);

    pstBlock = &g_astAllBlocks[pstAscii->ucBlockID];
    pucWidth = Hv_Drv_OsdResource_GetRealWidth(pstBlock);

    *ppMatrixAddr = Hv_Drv_OsdResource_GetBlockMatrixAddr(pstBlock);
    *pusOffset = Hv_Drv_OsdResource_SearchFontIndexByCode(pstAscii->ucBlockID, pstAscii->ausString[pstParam->usCurIndex]);
    *pucRealWidth = pucWidth[*pusOffset];

    return HV_TRUE;
}

VOID IterAsciiStepForward(OsdComponentIterator *pstIterator)
{
    OsdAsciiIterator *pstParam = (OsdAsciiIterator*)&pstIterator->pvArgs;

    ++pstParam->usCurIndex;
}

BOOL IterAsciiIsEnd(OsdComponentIterator *pstIterator)
{
    OsdAsciiString *pstAscii = NULL;
    OsdAsciiIterator *pstParam = (OsdAsciiIterator*)&pstIterator->pvArgs;

    pstAscii = Hv_Drv_OsdFont_GetComponentData(pstIterator->pstComponent);

    return pstParam->usCurIndex >= pstAscii->ucStringLength;
}

VOID IterAsciiReset(OsdComponentIterator *pstIterator)
{
    OsdAsciiIterator *pstParam = (OsdAsciiIterator*)&pstIterator->pvArgs;

    pstParam->usCurIndex = 0;
    return;
}

BOOL IterNumberGetCurrentCharacter(OsdComponentIterator *pstIterator, const UCHAR8 **ppMatrixAddr,
                                    USHORT16 *pusOffset, UCHAR8 *pucRealWidth)
{
    UCHAR8 ucCurIndex = 0;
    OsdNumber *pstNumber = NULL;
    const UCHAR8 *pucWidth = NULL;
    const OsdBlock *pstBlock = NULL;
    OsdNumberIterator *pstParam = (OsdNumberIterator*)pstIterator->pvArgs;

    pstNumber = (OsdNumber*)Hv_Drv_OsdFont_GetComponentData(pstIterator->pstComponent);

    pstBlock = &g_astAllBlocks[pstNumber->ucBlockID];
    pucWidth = Hv_Drv_OsdResource_GetRealWidth(pstBlock);

    *ppMatrixAddr = Hv_Drv_OsdResource_GetBlockMatrixAddr(pstBlock);

    ucCurIndex = pstParam->ucLen - 1 - pstParam->ucCurIndex;
    *pusOffset = pstParam->ausCharOffsets[ucCurIndex];

    *pucRealWidth = pucWidth[*pusOffset];

    return HV_TRUE;
}

VOID IterNumberStepForward(OsdComponentIterator *pstIterator)
{
    OsdNumberIterator *pstParam = (OsdNumberIterator*)pstIterator->pvArgs;

    ++pstParam->ucCurIndex;
}

BOOL IterNumberIsEnd(OsdComponentIterator *pstIterator)
{
    OsdNumberIterator *pstParam = (OsdNumberIterator*)pstIterator->pvArgs;

    return pstParam->ucCurIndex >= pstParam->ucLen;
}

VOID IterNumberReset(OsdComponentIterator *pstIterator)
{
    OsdNumberIterator *pstParam = (OsdNumberIterator*)pstIterator->pvArgs;

    pstParam->ucCurIndex = 0;
    return;
}

BOOL IterBmpIconGetCurrentCharacter(OsdComponentIterator *pstIterator, const UCHAR8 **ppMatrixAddr,
                                    USHORT16 *pusOffset, UCHAR8 *pucRealWidth)
{
    UCHAR8 ucCurIndex = 0;
    UCHAR8 ucColumns = 0;
    const USHORT16 *pusOffsetList = NULL;
    const OsdBlock *pstBlock = NULL;
    VOID *pvData = NULL;
    OsdComponentNode *pstComponent = pstIterator->pstComponent;
    OsdBmpIconIterator *pstParam = (OsdBmpIconIterator*)&pstIterator->pvArgs;
    OsdIconList *pstIconList = NULL;
    OsdBmpList *pstBmpList = NULL;

    pvData = Hv_Drv_OsdFont_GetComponentData(pstComponent);

    pstBlock = Hv_Drv_OsdFont_GetBlock(pstComponent);
    *ppMatrixAddr = Hv_Drv_OsdResource_GetBlockMatrixAddr(pstBlock);
    Hv_Drv_OsdResource_GetSize(pstBlock->usResourceSize, pucRealWidth, NULL);

    pusOffsetList = Hv_Drv_OsdResource_GetBlockCode(pstBlock);

    ucColumns = Hv_Drv_OsdFont_GetComponentColumns(pstComponent);
    switch (pstComponent->stCp.enType)
    {
        case OSD_CP_TYPE_BMP:
        {
            *pusOffset = pusOffsetList[((OsdBmp*)pvData)->usBMPOffset + ucColumns * pstParam->usCurRow + pstParam->usCurColumn];
            break;
        }
        case OSD_CP_TYPE_ICON:
        {
            *pusOffset = pusOffsetList[((OsdIcon*)pvData)->usIconOffset + ucColumns * pstParam->usCurRow + pstParam->usCurColumn];
            break;
        }
        case OSD_CP_TYPE_BMP_LIST:
        {
            pstBmpList = (OsdBmpList*)pvData;
            *pusOffset = pusOffsetList[pstBmpList->pstBmpList[pstBmpList->ucBmpCurID].usBMPOffset + ucColumns * pstParam->usCurRow + pstParam->usCurColumn];
            break;
        }
        case OSD_CP_TYPE_ICON_LIST:
        {
            pstIconList = (OsdIconList*)pvData;
            *pusOffset = pusOffsetList[pstIconList->pstIconList[pstIconList->ucIconCurID].usIconOffset + ucColumns * pstParam->usCurRow + pstParam->usCurColumn];
            break;
        }
        default:
        {
            break;
        }
    }

    return HV_TRUE;
}

VOID IterBmpIconStepForwardRow(OsdComponentIterator *pstIterator)
{
    OsdBmpIconIterator *pstParam = (OsdBmpIconIterator*)&pstIterator->pvArgs;

    ++pstParam->usCurRow;

    return;
}

VOID IterBmpIconStepForward(OsdComponentIterator *pstIterator)
{
    OsdBmpIconIterator *pstParam = (OsdBmpIconIterator*)&pstIterator->pvArgs;

    ++pstParam->usCurColumn;

    return;
}

BOOL IterBmpIconIsEnd(OsdComponentIterator *pstIterator)
{
    UCHAR8 ucRows = 0;
    UCHAR8 ucColumns = 0;
    OsdBmpIconIterator *pstParam = (OsdBmpIconIterator*)&pstIterator->pvArgs;

    ucRows = Hv_Drv_OsdFont_GetComponentRows(pstIterator->pstComponent);
    ucColumns = Hv_Drv_OsdFont_GetComponentColumns(pstIterator->pstComponent);

    return (pstParam->usCurRow >= ucRows) || (pstParam->usCurColumn >= ucColumns);
}

VOID IterBmpIconReset(OsdComponentIterator *pstIterator)
{
    UCHAR8 ucRows = 0;
    OsdBmpIconIterator *pstParam = (OsdBmpIconIterator*)&pstIterator->pvArgs;

    pstParam->usCurColumn = 0;

    ucRows = Hv_Drv_OsdFont_GetComponentRows(pstIterator->pstComponent);
    if (pstParam->usCurRow >= ucRows)
    {
        pstParam->usCurRow = 0;
    }

    return;
}

USHORT16 IterTextGetCode(OsdComponentIterator *pstIterator)
{
    OsdTextIterator *pstParam = (OsdTextIterator*)&pstIterator->pvArgs;

    return *(pstParam->pusString);
}

USHORT16 IterAsciiGetCode(OsdComponentIterator *pstIterator)
{
    OsdAsciiString *pstAscii = NULL;
    OsdAsciiIterator *pstParam = (OsdAsciiIterator*)&pstIterator->pvArgs;

    pstAscii = (OsdAsciiString*)Hv_Drv_OsdFont_GetComponentData(pstIterator->pstComponent);
    return pstAscii->ausString[pstParam->usCurIndex];
}

USHORT16 Hv_Drv_OsdFont_IterGetCode(OsdComponentIterator *pstIterator)
{
    OsdComponentNode *pstComponent = pstIterator->pstComponent;

    switch (pstComponent->stCp.enType)
    {
        case OSD_CP_TYPE_TEXT:
        case OSD_CP_TYPE_TEXT_LIST:
        {
            return IterTextGetCode(pstIterator);
        }
        case OSD_CP_TYPE_ASCII_STRING:
        {
            return IterAsciiGetCode(pstIterator);
        }
        default:
        {
            break;
        }
    }

    return 0;
}

BOOL Hv_Drv_OsdFont_IterGetCurrentCharacter(OsdComponentIterator *pstIterator, const UCHAR8 **ppMatrixAddr,
                                    USHORT16 *pusOffset, UCHAR8 *pucRealWidth)
{
    USHORT16 usCode = 0;
    OsdComponentNode *pstComponent = pstIterator->pstComponent;

    usCode = Hv_Drv_OsdFont_IterGetCode(pstIterator);
    if ((__NEWLINE__ == usCode) || (__WORDSPLIT__ == usCode))
    {
        *pucRealWidth = 0;
        return HV_TRUE;
    }

    switch (pstComponent->stCp.enType)
    {
        case OSD_CP_TYPE_TEXT:
        {
            return IterTextGetCurrentCharacter(pstIterator, ppMatrixAddr, pusOffset, pucRealWidth);
            break;
        }
        case OSD_CP_TYPE_TEXT_LIST:
        {
            return IterTextListGetCurrentCharacter(pstIterator, ppMatrixAddr, pusOffset, pucRealWidth);
            break;
        }
        case OSD_CP_TYPE_ASCII_STRING:
        {
            return IterAsciiGetCurrentCharacter(pstIterator, ppMatrixAddr, pusOffset, pucRealWidth);
            break;
        }
        case OSD_CP_TYPE_NUMBER:
        {
            return IterNumberGetCurrentCharacter(pstIterator, ppMatrixAddr, pusOffset, pucRealWidth);
            break;
        }
        case OSD_CP_TYPE_BMP:
        case OSD_CP_TYPE_BMP_LIST:
        case OSD_CP_TYPE_ICON:
        case OSD_CP_TYPE_ICON_LIST:
        {
            return IterBmpIconGetCurrentCharacter(pstIterator, ppMatrixAddr, pusOffset, pucRealWidth);
            break;
        }
        default:
        {
            break;
        }
    }

    return HV_FALSE;
}

VOID Hv_Drv_OsdFont_IterStepForwardRow(OsdComponentIterator *pstIterator)
{
    OsdComponentNode *pstComponent = pstIterator->pstComponent;

    switch (pstComponent->stCp.enType)
    {
        case OSD_CP_TYPE_BMP:
        case OSD_CP_TYPE_BMP_LIST:
        case OSD_CP_TYPE_ICON:
        case OSD_CP_TYPE_ICON_LIST:
        {
            return IterBmpIconStepForwardRow(pstIterator);
            break;
        }
        default:
        {
            return;
        }
    }
}

VOID Hv_Drv_OsdFont_IterStepForward(OsdComponentIterator *pstIterator)
{
    OsdComponentNode *pstComponent = pstIterator->pstComponent;

    switch (pstComponent->stCp.enType)
    {
        case OSD_CP_TYPE_TEXT:
        case OSD_CP_TYPE_TEXT_LIST:
        {
            return IterTextStepForward(pstIterator);
            break;
        }
        case OSD_CP_TYPE_ASCII_STRING:
        {
            return IterAsciiStepForward(pstIterator);
            break;
        }
        case OSD_CP_TYPE_NUMBER:
        {
            return IterNumberStepForward(pstIterator);
            break;
        }
        case OSD_CP_TYPE_BMP:
        case OSD_CP_TYPE_BMP_LIST:
        case OSD_CP_TYPE_ICON:
        case OSD_CP_TYPE_ICON_LIST:
        {
            return IterBmpIconStepForward(pstIterator);
            break;
        }
        default:
        {
            return;
        }
    }
}

BOOL Hv_Drv_OsdFont_IterIsEnd(OsdComponentIterator *pstIterator)
{
    OsdComponentNode *pstComponent = pstIterator->pstComponent;

    switch (pstComponent->stCp.enType)
    {
        case OSD_CP_TYPE_TEXT:
        case OSD_CP_TYPE_TEXT_LIST:
        {
            return IterTextIsEnd(pstIterator);
        }
        case OSD_CP_TYPE_ASCII_STRING:
        {
            return IterAsciiIsEnd(pstIterator);
        }
        case OSD_CP_TYPE_NUMBER:
        {
            return IterNumberIsEnd(pstIterator);
        }
        case OSD_CP_TYPE_BMP:
        case OSD_CP_TYPE_BMP_LIST:
        case OSD_CP_TYPE_ICON:
        case OSD_CP_TYPE_ICON_LIST:
        {
            return IterBmpIconIsEnd(pstIterator);
        }
        default:
        {
            break;
        }
    }

    return HV_TRUE;
}

VOID Hv_Drv_OsdFont_IterReset(OsdComponentIterator *pstIterator)
{
    OsdComponentNode *pstComponent = pstIterator->pstComponent;

    switch (pstComponent->stCp.enType)
    {
        case OSD_CP_TYPE_TEXT:
        {
            IterTextReset(pstIterator);
            break;
        }
        case OSD_CP_TYPE_TEXT_LIST:
        {
            IterTextListReset(pstIterator);
            break;
        }
        case OSD_CP_TYPE_ASCII_STRING:
        {
            IterAsciiReset(pstIterator);
            break;
        }
        case OSD_CP_TYPE_NUMBER:
        {
            IterNumberReset(pstIterator);
            break;
        }
        case OSD_CP_TYPE_BMP:
        case OSD_CP_TYPE_BMP_LIST:
        case OSD_CP_TYPE_ICON:
        case OSD_CP_TYPE_ICON_LIST:
        {
            IterBmpIconReset(pstIterator);
            break;
        }
        default:
        {
            break;
        }
    }

    return;
}

Status Hv_Drv_OsdFont_IterInit(OsdComponentIterator *pstIterator, OsdComponentNode *pstComponent)
{
    HV_MEMSET(pstIterator, 0, sizeof(*pstIterator));

    pstIterator->pstComponent = pstComponent;

    if (OSD_CP_TYPE_NUMBER == pstComponent->stCp.enType)
    {
        pstIterator->pvArgs = HV_MALLOC(sizeof(OsdNumberIterator));
        if (NULL == pstIterator->pvArgs)
        {
            OSD_DRV_LOG_ERROR("Failed to malloc marquee node param: %d bytes, not enough memory", sizeof(OsdNumberIterator));

            return HV_FAILURE;
        }
        HV_MEMSET(pstIterator->pvArgs, 0, sizeof(OsdNumberIterator));
    }

    Hv_Drv_OsdFont_IterReset(pstIterator);

    return HV_SUCCESS;
}

VOID Hv_Drv_OsdFont_IterFree(OsdComponentIterator *pstIterator)
{
    OsdComponentNode *pstComponent = NULL;

    if (NULL == pstIterator)
    {
        return;
    }

    pstComponent = pstIterator->pstComponent;
    if ((OSD_CP_TYPE_NUMBER == pstComponent->stCp.enType) && (NULL != pstIterator->pvArgs))
    {
        HV_FREE(pstIterator->pvArgs);
    }

    return;
}

Status Hv_Drv_OsdFont_IterCopy(OsdComponentIterator *pstDstIter, OsdComponentIterator *pstSrcIter)
{
    OsdComponentNode *pstComponent = pstSrcIter->pstComponent;
    Status uiStat = HV_FAILURE;

    if (OSD_CP_TYPE_NUMBER == pstComponent->stCp.enType)
    {
        if (NULL == pstDstIter->pvArgs)
        {
            uiStat = Hv_Drv_OsdFont_IterInit(pstDstIter, pstComponent);
            if (HV_FAILURE == uiStat)
            {
                return HV_FAILURE;
            }
        }

        pstDstIter->pstComponent = pstComponent;
        HV_MEMCPY(pstDstIter->pvArgs, pstSrcIter->pvArgs, sizeof(OsdNumberIterator));
    }
    else
    {
        HV_MEMCPY(pstDstIter, pstSrcIter, sizeof(OsdComponentIterator));
    }

    return HV_SUCCESS;
}

static OsdRowInfo *Hv_Drv_OsdFont_AllocRowInfo(VOID)
{
    struct LinkedList *pstEntry = NULL;
    OsdRowInfo *pstRowInfo = NULL;

    HV_ASSERT_TRUE_RET(s_pstFontParam->ucRowInfoFreeCount > 0, NULL);

    --s_pstFontParam->ucRowInfoFreeCount;

    pstEntry = Hv_Comm_LinkedList_PopHead(&s_pstFontParam->stRowInfoFreeList);
    pstRowInfo = Hv_Comm_LinkedList_Entry(pstEntry, OsdRowInfo, stEntry);
    HV_MEMSET(pstRowInfo, 0, sizeof(*pstRowInfo));
    Hv_Comm_LinkedList_Init(&pstRowInfo->stEntry);

    OSD_DRV_LOG_DEBUG("Alloc one OsdRowInfo, current free: %d", s_pstFontParam->ucRowInfoFreeCount);
    return pstRowInfo;
}

static VOID Hv_Drv_OsdFont_FreeRowInfo(OsdRowInfo *pstRowInfo)
{
    HV_ASSERT_TRUE_VOID(pstRowInfo != NULL);

    Hv_Comm_LinkedList_AddHead(&s_pstFontParam->stRowInfoFreeList, &pstRowInfo->stEntry);
    ++s_pstFontParam->ucRowInfoFreeCount;
    OSD_DRV_LOG_DEBUG("Free one OsdRowInfo, current free: %d", s_pstFontParam->ucRowInfoFreeCount);
}

#if (HV_PROJECT_CONFIG_OSD_MARQUEE == HV_CONFIG_ON)

VOID Hv_Drv_OsdFont_MarqueeMarkSlotsFreed(VOID)
{
    struct LinkedList *pstNode = NULL;
    OsdMarqueeNode *pstMarquee = NULL;
    OsdComponentNode *pstComponent = NULL;

    Hv_Comm_LinkedList_ForEach(&s_pstFontParam->stMarqueeHead, pstNode)
    {
        pstMarquee = Hv_Comm_LinkedList_Entry(pstNode, OsdMarqueeNode, stEntry);
        pstComponent = pstMarquee->stIterator.pstComponent;

        pstMarquee->bMallocSlots = HV_TRUE;
        OSD_DRV_LOG_DEBUG("Marquee mark component %d-%d to alloc slots", pstComponent->stCp.enType, pstComponent->stCp.usIndex);
    }

    return;
}

VOID Hv_Drv_OsdFont_MarqueeMarkSlotsFreedByRow(OsdRowInfo *pstRowInfo)
{
    struct LinkedList *pstNode = NULL;
    OsdMarqueeNode *pstMarquee = NULL;
    OsdComponentNode *pstComponent = NULL;

    Hv_Comm_LinkedList_ForEach(&s_pstFontParam->stMarqueeHead, pstNode)
    {
        pstMarquee = Hv_Comm_LinkedList_Entry(pstNode, OsdMarqueeNode, stEntry);
        pstComponent = pstMarquee->stIterator.pstComponent;

        if (pstRowInfo == pstComponent->pstRowInfo)
        {
            pstMarquee->bMallocSlots = HV_TRUE;
            OSD_DRV_LOG_DEBUG("By Row: Marquee mark component %d-%d to alloc slots", pstComponent->stCp.enType, pstComponent->stCp.usIndex);
        }
    }

    return;
}

VOID Hv_Drv_OsdFont_AppendMarqueeNode(OsdMarqueeNode *pstNode)
{
    HV_ASSERT_TRUE_VOID(NULL != pstNode);

    Hv_Comm_LinkedList_AddTail(&s_pstFontParam->stMarqueeHead, &pstNode->stEntry);
    return;
}

VOID Hv_Drv_OsdFont_RemoveMarqueeNode(OsdMarqueeNode *pstMarquee)
{
    OsdMarqueeType enMarqueeType = 0;
    OsdComponentNode *pstComponent = NULL;

    if (NULL == pstMarquee)
    {
        return;
    }

    pstComponent = pstMarquee->stIterator.pstComponent;

    /* integrity check */
    HV_ASSERT_TRUE_VOID(pstMarquee == pstComponent->pstMarquee);

    /* Delete from global list */
    Hv_Comm_LinkedList_Del(&s_pstFontParam->stMarqueeHead, &pstMarquee->stEntry);

    Hv_Drv_OsdFont_IterFree(&pstMarquee->stIterator);

    enMarqueeType = Hv_Drv_OsdFont_GetMarqueeType(pstComponent);
    if ((OSD_MARQUEE_TYPE_SLIDING_NEXT == enMarqueeType) && (NULL != pstMarquee->pvMarqueeArgs))
    {
        HV_FREE(pstMarquee->pvMarqueeArgs);
    }
    HV_FREE(pstMarquee);

    pstComponent->pstMarquee = NULL;

    if (OSD_MARQUEE_TYPE_DEFAULT == pstComponent->enMarqueeType)
    {
        pstComponent->enMarqueeType = OSD_MARQUEE_TYPE_NONE;
        pstComponent->bStartAfterCreate = HV_FALSE;
    }

    OSD_DRV_LOG_DEBUG("Remove marquee of component %d-%d", pstComponent->stCp.enType, pstComponent->stCp.usIndex);
    return;
}

VOID Hv_Drv_OsdFont_RemoveAutoMarqueeNodes(VOID)
{
    struct LinkedList *pstNode = NULL;
    struct LinkedList *pstNext = NULL;
    OsdMarqueeNode *pstMarquee = NULL;
    OsdComponentNode *pstComponent = NULL;

    Hv_Comm_LinkedList_ForEachSafe(&s_pstFontParam->stMarqueeHead, pstNext, pstNode)
    {
        pstMarquee = Hv_Comm_LinkedList_Entry(pstNode, OsdMarqueeNode, stEntry);
        pstComponent = pstMarquee->stIterator.pstComponent;

        if (pstComponent->enMarqueeType == OSD_MARQUEE_TYPE_DEFAULT)
        {
            Hv_Drv_OsdFont_RemoveMarqueeNode(pstMarquee);
            pstComponent->enMarqueeType = OSD_MARQUEE_TYPE_NONE;
        }
    }
}

BOOL Hv_Drv_OsdFont_NeedMarquee(VOID)
{
    struct LinkedList *pstNode = NULL;
    OsdMarqueeNode *pstMarquee = NULL;
    BOOL bHasRunning = HV_FALSE;

    Hv_Comm_LinkedList_ForEach(&s_pstFontParam->stMarqueeHead, pstNode)
    {
        pstMarquee = Hv_Comm_LinkedList_Entry(pstNode, OsdMarqueeNode, stEntry);

        if (OSD_MARQUEE_STAT_RUNNING == pstMarquee->enState)
        {
            bHasRunning = HV_TRUE;
            break;
        }
    }

    //OSD_LOG_INFO("need marquee: %d", bHasRunning);
    return bHasRunning;
}

Status Hv_Drv_OsdFont_MarqueeTypeInitNode(OsdMarqueeNode *pstMarquee, OsdMarqueeType enMarqueeType)
{
    OsdMarqueeSlidingNextParam *pstParam = NULL;

    switch (enMarqueeType)
    {
        case OSD_MARQUEE_TYPE_SCROLLING_LEFT:
        {
            *(OsdMarqueeDirection*)&pstMarquee->pvMarqueeArgs = OSD_MARQUEE_LEFT;
            break;
        }
        case OSD_MARQUEE_TYPE_SCROLLING_RIGHT:
        {
            *(OsdMarqueeDirection*)&pstMarquee->pvMarqueeArgs = OSD_MARQUEE_RIGHT;
            break;
        }
        case OSD_MARQUEE_TYPE_SLIDING_NEXT:
        {
            pstParam = HV_MALLOC(sizeof(*pstParam));
            if (NULL == pstParam)
            {
                OSD_DRV_LOG_ERROR("Failed to malloc marquee type param: %d bytes, not enough memory", sizeof(*pstParam));
                return HV_FAILURE;
            }

            pstParam->ucPart = OSD_MARQUEE_PART_FIRST;
            pstParam->usDestX = 0;
            pstMarquee->pvMarqueeArgs = pstParam;
            break;
        }
        default:
        {
            break;
        }
    }

    return HV_SUCCESS;
}

VOID Hv_Drv_OsdFont_MarqueeNextSetParam(OsdComponentNode *pstNode, OsdMarqueeDirection enDirection, INT32 iNewValue)
{
    OsdMarqueeNode *pstMarquee = pstNode->pstMarquee;
    OsdMarqueeSlidingNextParam *pstParam = NULL;
    OsdMarqueeType enMarqueeType = 0;

    if (NULL == pstMarquee)
    {
        return;
    }

    /* only sliding next support direction setting */
    enMarqueeType = Hv_Drv_OsdFont_GetMarqueeType(pstNode);
    if (OSD_MARQUEE_TYPE_SLIDING_NEXT == enMarqueeType)
    {
        pstParam = pstMarquee->pvMarqueeArgs;
        pstParam->ucDirection = enDirection;
        pstParam->iNextValue = iNewValue;
    }

    return;
}

VOID Hv_Drv_OsdFont_StartMarquee(OsdComponentNode *pstNode)
{
    OsdMarqueeNode *pstMarquee = pstNode->pstMarquee;

    if (NULL == pstMarquee)
    {
        OSD_DRV_LOG_DEBUG("Try to start a nonexist Marquee node %d-%d", pstNode->stCp.enType, pstNode->stCp.usIndex);
        return;
    }

    /* integrity check */
    HV_ASSERT_TRUE_VOID(pstMarquee->stIterator.pstComponent == pstNode);

    OSD_DRV_LOG_DEBUG("Marquee set componnet %d-%d state to RUNNING", pstNode->stCp.enType, pstNode->stCp.usIndex);
    pstMarquee->enState = OSD_MARQUEE_STAT_RUNNING;
    return;
}

VOID Hv_Drv_OsdFont_PauseMarquee(OsdComponentNode *pstNode)
{
    OsdMarqueeNode *pstMarquee = pstNode->pstMarquee;

    if (NULL == pstMarquee)
    {
        OSD_DRV_LOG_WARN("Try to pause a nonexist Marquee node %d-%d", pstNode->stCp.enType, pstNode->stCp.usIndex);
        return;
    }

    /* integrity check */
    HV_ASSERT_TRUE_VOID(pstMarquee->stIterator.pstComponent == pstNode);

    OSD_DRV_LOG_DEBUG("Marquee set componnet %d-%d state to PAUSED", pstNode->stCp.enType, pstNode->stCp.usIndex);
    pstMarquee->enState = OSD_MARQUEE_STAT_PAUSED;
    return;
}

VOID Hv_Drv_OsdFont_ResetMarquee(OsdComponentNode *pstNode)
{
    OsdMarqueeNode *pstMarquee = pstNode->pstMarquee;

    if(NULL == pstMarquee)
    {
        return;
    }

    /* integrity check */
    HV_ASSERT_TRUE_VOID(pstNode == pstMarquee->stIterator.pstComponent);

    Hv_Drv_OsdFont_CalculateComponentBox(pstNode, HV_TRUE);
    pstMarquee->usStartPos = 0;

    OSD_DRV_LOG_DEBUG("Marquee reset component %d-%d realwidth: %d realX: %d startpos: %d",
            pstNode->stCp.enType, pstNode->stCp.usIndex, pstMarquee->usRealWidth,
            pstMarquee->usRealX, pstMarquee->usStartPos);

    /* set state to PAUSED */
    Hv_Drv_OsdFont_PauseMarquee(pstNode);

    Hv_Drv_OsdFont_MarqueeNodeStep(pstMarquee, HV_FALSE);
    Hv_Drv_OsdResource_SetChangeRange(OSD_SRAM_CHANGE_FULL, 0, 0);

    return;
}

VOID Hv_Drv_OsdFont_ResetAll(VOID)
{
    struct LinkedList *pstNode = NULL;
    OsdMarqueeNode *pstMarquee = NULL;

    Hv_Comm_LinkedList_ForEach(&s_pstFontParam->stMarqueeHead, pstNode)
    {
        pstMarquee = Hv_Comm_LinkedList_Entry(pstNode, OsdMarqueeNode, stEntry);

        Hv_Drv_OsdFont_ResetMarquee(pstMarquee->stIterator.pstComponent);
    }

    return;
}

VOID Hv_Drv_OsdFont_ResumeAll(OsdMarqueeDirection enDirection, INT32 iNextValue)
{
    struct LinkedList *pstNode = NULL;
    OsdMarqueeNode *pstMarquee = NULL;

    Hv_Comm_LinkedList_ForEach(&s_pstFontParam->stMarqueeHead, pstNode)
    {
        pstMarquee = Hv_Comm_LinkedList_Entry(pstNode, OsdMarqueeNode, stEntry);

        Hv_Drv_OsdFont_MarqueeNextSetParam(pstMarquee->stIterator.pstComponent, enDirection, iNextValue);
        Hv_Drv_OsdFont_StartMarquee(pstMarquee->stIterator.pstComponent);
    }

    return;
}

VOID Hv_Drv_OsdFont_MarqueeDebugNode(OsdMarqueeNode *pstMarquee, UCHAR8 ucLogLevel)
{
    OsdMarqueeType enMarqueeType = 0;
    OsdMarqueeSlidingParam *pstParam = NULL;
    OsdComponentNode *pstComponent = pstMarquee->stIterator.pstComponent;
    BOOL bSliding = HV_FALSE;

    pstParam = (OsdMarqueeSlidingParam*)&pstMarquee->pvMarqueeArgs;

    enMarqueeType = Hv_Drv_OsdFont_GetMarqueeType(pstComponent);
    bSliding = (enMarqueeType == OSD_MARQUEE_TYPE_SLIDING_LEFT) || (enMarqueeType == OSD_MARQUEE_TYPE_SLIDING_RIGHT);
    if (HV_LOG_LEVEL_INFO == ucLogLevel)
    {
        OSD_LOG_INFO("Marquee component(0x%x) %d-%d, status: %s replace slot: %s realWidth: %d realX: %d startpos: %d dest realX: %d dest startPos: %d",
                pstComponent, pstComponent->stCp.enType, pstComponent->stCp.usIndex,
                pstMarquee->enState == OSD_MARQUEE_STAT_RUNNING ? "RUNNING" : "PAUSED",
                pstMarquee->bMallocSlots ? "No" : "Yes", pstMarquee->usRealWidth,
                pstMarquee->usRealX, pstMarquee->usStartPos,
                bSliding ? pstParam->usDestRealX : -1, bSliding ? pstParam->usDestStartPos : -1);
    }
    else
    {
        OSD_LOG_DEBUG("Marquee component(0x%x) %d-%d, status: %s replace slot: %s realWidth: %d realX: %d startpos: %d dest realX: %d dest startPos: %d",
                pstComponent, pstComponent->stCp.enType, pstComponent->stCp.usIndex,
                pstMarquee->enState == OSD_MARQUEE_STAT_RUNNING ? "RUNNING" : "PAUSED",
                pstMarquee->bMallocSlots ? "No" : "Yes", pstMarquee->usRealWidth,
                pstMarquee->usRealX, pstMarquee->usStartPos,
                bSliding ? pstParam->usDestRealX : -1, bSliding ? pstParam->usDestStartPos : -1);

    }
}

VOID Hv_Drv_OsdFont_MarqueeDebug(VOID)
{
    struct LinkedList *pstNode = NULL;
    OsdMarqueeNode *pstMarquee = NULL;

    Hv_Comm_LinkedList_ForEach(&s_pstFontParam->stMarqueeHead, pstNode)
    {
        pstMarquee = Hv_Comm_LinkedList_Entry(pstNode, OsdMarqueeNode, stEntry);

        Hv_Drv_OsdFont_MarqueeDebugNode(pstMarquee, HV_LOG_LEVEL_INFO);
    }

    return;
}

Status Hv_Drv_OsdFont_MarqueeComponentInitNode(OsdMarqueeNode *pstMarquee, OsdComponentNode *pstComponent)
{
    pstComponent->pstMarquee = pstMarquee;;

    return Hv_Drv_OsdFont_IterInit(&pstMarquee->stIterator, pstComponent);
}

OsdMarqueeNode *Hv_Drv_OsdFont_AllocMarqueeNode(OsdComponentNode *pstComponent)
{
    OsdMarqueeNode *pstNode = NULL;
    Status uiStat = HV_FAILURE;

    pstNode = HV_MALLOC(sizeof(OsdMarqueeNode));
    if(NULL == pstNode)
    {
        OSD_DRV_LOG_ERROR("Failed to malloc marquee node. not enough memory");
        return NULL;
    }

    HV_MEMSET(pstNode, 0, sizeof(OsdMarqueeNode));
    Hv_Comm_LinkedList_Init(&pstNode->stEntry);
    Hv_Drv_OsdFont_AppendMarqueeNode(pstNode);

    if (pstComponent->bStartAfterCreate)
    {
        pstNode->enState = OSD_MARQUEE_STAT_RUNNING;
    }
    else
    {
        pstNode->enState = OSD_MARQUEE_STAT_PAUSED;
    }
    pstNode->bMallocSlots = HV_TRUE;

    uiStat = Hv_Drv_OsdFont_MarqueeTypeInitNode(pstNode, pstComponent->enMarqueeType);
    if (HV_FAILURE == uiStat)
    {
        HV_FREE(pstNode);
        return NULL;
    }

    uiStat = Hv_Drv_OsdFont_MarqueeComponentInitNode(pstNode, pstComponent);
    if (HV_FAILURE == uiStat)
    {
        HV_FREE(pstNode);
        return NULL;
    }

    Hv_Drv_OsdFont_IterReset(&pstNode->stIterator);

    return pstNode;
}

VOID Hv_Drv_OsdFont_ResetAllMarqueeContents(VOID)
{
    struct LinkedList *pstNode = NULL;
    struct LinkedList *pstNext = NULL;
    OsdMarqueeNode *pstMarquee = NULL;

    Hv_Comm_LinkedList_ForEachSafe(&s_pstFontParam->stMarqueeHead, pstNext, pstNode)
    {
        pstMarquee = Hv_Comm_LinkedList_Entry(pstNode, OsdMarqueeNode, stEntry);
        Hv_Drv_OsdFont_IterReset(&pstMarquee->stIterator);
    }
}

static OsdIndexColumn* Hv_Drv_OsdFont_DrawMarqueeText(OsdMarqueeNode *pstMarquee, BOOL bIsUpdate)
{
    OsdComponentIterator *pstIterator = &pstMarquee->stIterator;
    OsdRowInfo *pstRowInfo = pstMarquee->pstRowInfo;
    OsdComponentNode *pstNode = pstIterator->pstComponent;
    OsdIndexColumn *pstColumn = pstMarquee->pstColumn;
    OsdIndexColumn *pstInputColumn = pstColumn;
    UCHAR8 ucIndex = 0;
    UCHAR8 ucSlots = 0;
    UCHAR8 ucWidth = 0;
    UCHAR8 ucRealWidth = 0;
    UCHAR8 ucColorIndex = 0;
    USHORT16 usX = 0;
    const UCHAR8 *pucMatrixAddr = NULL;
    USHORT16 usOffset = 0;
    USHORT16 usSlot = 0;
    BOOL bReadOut = HV_FALSE;
    BOOL bNeedEnd = HV_TRUE;
    UCHAR8 ucRmd = 0;
    USHORT16 usAccWidth = 0;
    USHORT16 usTotalWidth = 0;
    OsdMarqueeType enMarqueeType = 0;

    OSD_DRV_LOG_DEBUG("Draw Belowing Marquee node:");
    Hv_Drv_OsdFont_MarqueeDebugNode(pstMarquee, HV_LOG_LEVEL_VERBOSE);

    Hv_Drv_OsdShift_SetReplaceEnable(pstMarquee->pstColumn, pstMarquee->bMallocSlots ? HV_FALSE : HV_TRUE);

    enMarqueeType = Hv_Drv_OsdFont_GetMarqueeType(pstNode);

    usX = Hv_Drv_OsdFont_GetComponentX(pstNode);
    ucColorIndex = Hv_Drv_OsdFont_GetComponentColorIndex(pstNode);

    Hv_Drv_OsdResource_GetSize(pstRowInfo->enSize, &ucWidth, NULL);

    /* step 1. fill empty slots till first shown pixel */
    if (pstMarquee->usRealX > usX)
    {
        OSD_DRV_LOG_DEBUG("Marquee Step-1: fill empty slots from %d to %d", usX, pstMarquee->usRealX);

        ucSlots = (pstMarquee->usRealX - usX) / ucWidth;
        for (ucIndex = 0; ucIndex < ucSlots; ++ucIndex)
        {
            bReadOut = Hv_Drv_OsdShift_WriteBlank(ucWidth);
            if ((HV_TRUE == bReadOut) && !Hv_Drv_OsdShift_IsReplace())
            {
                usSlot = Hv_Drv_OsdFont_TakeCurrentSlot(pstRowInfo, bIsUpdate);
                Hv_Cal_OsdIndex_SetColIndex(pstColumn, usSlot);
                Hv_Cal_OsdIndex_SetColType(pstColumn, OSD_COLUMN_FONT);
                Hv_Cal_OsdIndex_SetFontColColorIndex(pstColumn, ucColorIndex);

                OSD_DRV_LOG_DEBUG("Draw Text column 0x%x index %u colorIndex %d delta %d",
                        pstColumn, Hv_Cal_OsdIndex_GetColIndex(pstColumn), ucColorIndex,
                        Hv_Cal_OsdIndex_GetColDelta(pstColumn));

                pstColumn++;
            }
        }

        ucRmd = (pstMarquee->usRealX - usX) % ucWidth;
        if (0 != ucRmd)
        {
            Hv_Drv_OsdShift_WriteBlank(ucRmd);
        }

        usTotalWidth = pstMarquee->usRealX - usX;
    }

    /* step 2. skip the first few chars */
    while ((usAccWidth < pstMarquee->usStartPos) && (!Hv_Drv_OsdFont_IterIsEnd(pstIterator)))
    {
        OSD_DRV_LOG_DEBUG("Step 2: Accumulative width: %d", usAccWidth);

        Hv_Drv_OsdFont_IterGetCurrentCharacter(pstIterator, &pucMatrixAddr, &usOffset, &ucRealWidth);
        usAccWidth += ucRealWidth;
        Hv_Drv_OsdFont_IterStepForward(pstIterator);
    }
    if (usAccWidth > pstMarquee->usStartPos)
    {
        Hv_Drv_OsdShift_DropNext();
        Hv_Drv_OsdShift_WriteBlank(ucWidth - (pstMarquee->usStartPos - (usAccWidth - ucRealWidth)));
        Hv_Drv_OsdShift_WriteCharacter(pstRowInfo, pucMatrixAddr, usOffset, ucRealWidth);

        usAccWidth -= pstMarquee->usStartPos;
        usTotalWidth += usAccWidth;
    }
    else
    {
        usAccWidth = 0;
    }

    if (pstMarquee->usStartPos > 0)
    {
        //OSD_DRV_LOG_DEBUG("Marquee Step-2: skip first %d characters", pusString - pusOrigString);
    }

    /* step 3. write the front half chars */
    while ((usTotalWidth < pstNode->stBox.usW) && (!Hv_Drv_OsdFont_IterIsEnd(pstIterator)))
    {
        OSD_DRV_LOG_DEBUG("Marquee Step-3: total width: %d box width: %d", usTotalWidth, pstNode->stBox.usW);

        Hv_Drv_OsdFont_IterGetCurrentCharacter(pstIterator, &pucMatrixAddr, &usOffset, &ucRealWidth);

        usTotalWidth += ucRealWidth;

        if (usTotalWidth > pstNode->stBox.usW)
        {
            bReadOut = Hv_Drv_OsdShift_WriteCharacter(pstRowInfo, pucMatrixAddr, usOffset,
                    ucRealWidth - (usTotalWidth - pstNode->stBox.usW));
        }
        else
        {
            bReadOut = Hv_Drv_OsdShift_WriteCharacter(pstRowInfo, pucMatrixAddr, usOffset, ucRealWidth);
        }

        if ((HV_TRUE == bReadOut) && !Hv_Drv_OsdShift_IsReplace())
        {
            usSlot = Hv_Drv_OsdFont_TakeCurrentSlot(pstRowInfo, bIsUpdate);
            Hv_Cal_OsdIndex_SetColIndex(pstColumn, usSlot);
            Hv_Cal_OsdIndex_SetColType(pstColumn, OSD_COLUMN_FONT);
            Hv_Cal_OsdIndex_SetFontColColorIndex(pstColumn, ucColorIndex);

            OSD_DRV_LOG_DEBUG("Draw Text column 0x%x index %u colorIndex %d delta %d",
                    pstColumn, Hv_Cal_OsdIndex_GetColIndex(pstColumn), ucColorIndex,
                    Hv_Cal_OsdIndex_GetColDelta(pstColumn));

            pstColumn++;
        }

        Hv_Drv_OsdFont_IterStepForward(pstIterator);
    }

    if (usTotalWidth < pstNode->stBox.usW)
    {
        ucSlots = 0;
        ucRmd = 0;
        /* step 4. fill middle blank area */
        if (((enMarqueeType != OSD_MARQUEE_TYPE_CYCLING_LEFT) &&
                (enMarqueeType != OSD_MARQUEE_TYPE_CYCLING_RIGHT)) ||
                (usTotalWidth + pstMarquee->usStartPos < pstNode->stBox.usW))
        {
            if ((enMarqueeType == OSD_MARQUEE_TYPE_CYCLING_LEFT) ||
                    (enMarqueeType == OSD_MARQUEE_TYPE_CYCLING_RIGHT))
            {
                ucSlots = (pstNode->stBox.usW - usTotalWidth - pstMarquee->usStartPos) / ucWidth;
                ucRmd = (pstNode->stBox.usW - usTotalWidth - pstMarquee->usStartPos) % ucWidth;
            }
            else
            {
                ucSlots = (pstNode->stBox.usW - usTotalWidth) / ucWidth;
                ucRmd = (pstNode->stBox.usW - usTotalWidth) % ucWidth;
            }

            OSD_DRV_LOG_DEBUG("Marquee Step-4: fill %d blank pixels", ucSlots * ucWidth + ucRmd);

            for (ucIndex = 0; ucIndex < ucSlots; ++ucIndex)
            {
                bReadOut = Hv_Drv_OsdShift_WriteBlank(ucWidth);
                if ((HV_TRUE == bReadOut) && !Hv_Drv_OsdShift_IsReplace())
                {
                    usSlot = Hv_Drv_OsdFont_TakeCurrentSlot(pstRowInfo, bIsUpdate);
                    Hv_Cal_OsdIndex_SetColIndex(pstColumn, usSlot);
                    Hv_Cal_OsdIndex_SetColType(pstColumn, OSD_COLUMN_FONT);
                    Hv_Cal_OsdIndex_SetFontColColorIndex(pstColumn, ucColorIndex);

                    OSD_DRV_LOG_DEBUG("Draw Text column 0x%x index %u colorIndex %d delta %d",
                            pstColumn, Hv_Cal_OsdIndex_GetColIndex(pstColumn), ucColorIndex,
                            Hv_Cal_OsdIndex_GetColDelta(pstColumn));

                    pstColumn++;
                }
            }

            if (0 != ucRmd)
            {
                bReadOut = Hv_Drv_OsdShift_WriteBlank(ucRmd);
                if ((HV_TRUE == bReadOut) && !Hv_Drv_OsdShift_IsReplace())
                {
                    usSlot = Hv_Drv_OsdFont_TakeCurrentSlot(pstRowInfo, bIsUpdate);
                    Hv_Cal_OsdIndex_SetColIndex(pstColumn, usSlot);
                    Hv_Cal_OsdIndex_SetColType(pstColumn, OSD_COLUMN_FONT);
                    Hv_Cal_OsdIndex_SetFontColColorIndex(pstColumn, ucColorIndex);

                    OSD_DRV_LOG_DEBUG("Draw Text column 0x%x index %u colorIndex %d delta %d",
                            pstColumn, Hv_Cal_OsdIndex_GetColIndex(pstColumn), ucColorIndex,
                            Hv_Cal_OsdIndex_GetColDelta(pstColumn));

                    pstColumn++;
                }
            }

            usTotalWidth += ucSlots * ucWidth + ucRmd;
        }

        /* Step 5. write the latter half chars */
        if (((enMarqueeType == OSD_MARQUEE_TYPE_CYCLING_LEFT) ||
                (enMarqueeType == OSD_MARQUEE_TYPE_CYCLING_RIGHT)) &&
                (pstMarquee->usStartPos > 0))
        {
            /* write gap */
            if (ucSlots * ucWidth + ucRmd < HV_OSD_CONFIG_MARQUEE_GAP)
            {
                ucRealWidth = HV_OSD_CONFIG_MARQUEE_GAP - ucRmd - ucSlots * ucWidth;
                if (usTotalWidth + ucRealWidth > pstNode->stBox.usW)
                {
                    ucRealWidth = pstNode->stBox.usW - usTotalWidth;
                }

                OSD_DRV_LOG_DEBUG("Marquee Step-5: write gap %d", ucRealWidth);

                bReadOut = Hv_Drv_OsdShift_WriteBlank(ucRealWidth);
                if ((HV_TRUE == bReadOut) && !Hv_Drv_OsdShift_IsReplace())
                {
                    usSlot = Hv_Drv_OsdFont_TakeCurrentSlot(pstRowInfo, bIsUpdate);
                    Hv_Cal_OsdIndex_SetColIndex(pstColumn, usSlot);
                    Hv_Cal_OsdIndex_SetColType(pstColumn, OSD_COLUMN_FONT);
                    Hv_Cal_OsdIndex_SetFontColColorIndex(pstColumn, ucColorIndex);

                    OSD_DRV_LOG_DEBUG("Draw Text column 0x%x index %u colorIndex %d delta %d",
                            pstColumn, Hv_Cal_OsdIndex_GetColIndex(pstColumn), ucColorIndex,
                            Hv_Cal_OsdIndex_GetColDelta(pstColumn));

                    pstColumn++;
                }
                usTotalWidth += ucRealWidth;
            }

            Hv_Drv_OsdFont_IterReset(pstIterator);

            while ((usTotalWidth < pstNode->stBox.usW))
            {
                Hv_Drv_OsdFont_IterGetCurrentCharacter(pstIterator, &pucMatrixAddr, &usOffset, &ucRealWidth);

                usTotalWidth += ucRealWidth;

                if (usTotalWidth > pstNode->stBox.usW)
                {
                    bReadOut = Hv_Drv_OsdShift_WriteCharacter(pstRowInfo, pucMatrixAddr, usOffset,
                            ucRealWidth - (usTotalWidth - pstNode->stBox.usW));
                }
                else
                {
                    bReadOut = Hv_Drv_OsdShift_WriteCharacter(pstRowInfo, pucMatrixAddr, usOffset, ucRealWidth);
                }

                if ((HV_TRUE == bReadOut) && !Hv_Drv_OsdShift_IsReplace())
                {
                    usSlot = Hv_Drv_OsdFont_TakeCurrentSlot(pstRowInfo, bIsUpdate);
                    Hv_Cal_OsdIndex_SetColIndex(pstColumn, usSlot);
                    Hv_Cal_OsdIndex_SetColType(pstColumn, OSD_COLUMN_FONT);
                    Hv_Cal_OsdIndex_SetFontColColorIndex(pstColumn, ucColorIndex);

                    OSD_DRV_LOG_DEBUG("Draw Text column 0x%x index %u colorIndex %d delta %d",
                            pstColumn, Hv_Cal_OsdIndex_GetColIndex(pstColumn), ucColorIndex,
                            Hv_Cal_OsdIndex_GetColDelta(pstColumn));

                    pstColumn++;
                }

                Hv_Drv_OsdFont_IterStepForward(pstIterator);
            }
        }
    }

    /* Step 6. End transaction */
    ucRealWidth = Hv_Drv_OsdShift_EndTransaction(HV_FALSE);
    if (0 != ucRealWidth && !Hv_Drv_OsdShift_IsReplace())
    {
        OSD_DRV_LOG_DEBUG("Marquee Step-6: remaining width: %d", ucRealWidth);

        usSlot = Hv_Drv_OsdFont_TakeCurrentSlot(pstRowInfo, bIsUpdate);
        Hv_Cal_OsdIndex_SetColIndex(pstColumn, usSlot);
        Hv_Cal_OsdIndex_SetColType(pstColumn, OSD_COLUMN_FONT);
        Hv_Cal_OsdIndex_SetFontColColorIndex(pstColumn, ucColorIndex);
        Hv_Cal_OsdIndex_SetColDelta(pstColumn, HV_FALSE, ucWidth - ucRealWidth);

        OSD_DRV_LOG_DEBUG("Draw Text column 0x%x index %u colorIndex %d delta %d",
                pstColumn, Hv_Cal_OsdIndex_GetColIndex(pstColumn), ucColorIndex,
                Hv_Cal_OsdIndex_GetColDelta(pstColumn));

        pstColumn++;
    }

    /* reset */
    Hv_Drv_OsdFont_IterReset(pstIterator);

    OSD_DRV_LOG_DEBUG("Marquee mark component %d-%d to replace slots", pstNode->stCp.enType, pstNode->stCp.usIndex);
    pstMarquee->bMallocSlots = HV_FALSE;

    if (Hv_Drv_OsdShift_IsReplace())
    {
        pstColumn = Hv_Drv_OsdShift_GetCurrentColumn();
    }

    //OSD_DRV_LOG_INFO("Draw Marquee Text total %d columns", pstColumn - pstInputColumn);
    //Hv_Drv_OsdFont_MarqueeDebugNode(pstMarquee, HV_LOG_LEVEL_INFO);

    Hv_Drv_OsdShift_SetReplaceEnable(pstMarquee->pstColumn, HV_FALSE);

    return pstColumn;
}

static BOOL Hv_Drv_OsdFont_MarqueeCyclingLeftUpdateStep(OsdMarqueeNode *pstMarquee)
{
    OsdComponentNode *pstNode = pstMarquee->stIterator.pstComponent;
    UCHAR8 ucStep = HV_OSD_CONFIG_MARQUEE_STEP;

    if (pstMarquee->usRealX > ucStep)
    {
        pstMarquee->usRealX -= ucStep;
        pstMarquee->usStartPos = 0;
    }
    else if (pstMarquee->usRealX > 0)
    {
        pstMarquee->usStartPos = ucStep - pstMarquee->usRealX;
        pstMarquee->usRealX = 0;
    }
    else
    {
        pstMarquee->usStartPos += ucStep;

        if (pstMarquee->usRealWidth <= pstMarquee->usStartPos)
        {
            if (pstMarquee->usStartPos < pstNode->stBox.usW)
            {
                pstMarquee->usRealX = pstNode->stBox.usW - pstMarquee->usStartPos;
            }
            else
            {
                pstMarquee->usRealX = HV_OSD_CONFIG_MARQUEE_GAP - (pstMarquee->usStartPos - pstMarquee->usRealWidth);
            }
            pstMarquee->usStartPos = 0;
        }
    }

    return HV_FALSE;
}

static BOOL Hv_Drv_OsdFont_MarqueeCyclingRightUpdateStep(OsdMarqueeNode *pstMarquee)
{
    OsdComponentNode *pstNode = pstMarquee->stIterator.pstComponent;
    UCHAR8 ucStep = HV_OSD_CONFIG_MARQUEE_STEP;
    UCHAR8 ucGap = HV_OSD_CONFIG_MARQUEE_GAP;
    USHORT16 usExcess = 0;
    USHORT16 usWithinbox = 0;

    if (pstMarquee->usStartPos > ucStep)
    {
        pstMarquee->usStartPos -= ucStep;
    }
    else if (pstMarquee->usStartPos > 0)
    {
        pstMarquee->usRealX = ucStep - pstMarquee->usStartPos;
        pstMarquee->usStartPos = 0;
    }
    else
    {
        pstMarquee->usRealX += ucStep;
        if (pstMarquee->usRealWidth + pstMarquee->usRealX > pstNode->stBox.usW)
        {
            usExcess = pstMarquee->usRealWidth + pstMarquee->usRealX - pstNode->stBox.usW;

            if (pstMarquee->usRealX > ucGap)
            {
                pstMarquee->usStartPos = pstNode->stBox.usW - pstMarquee->usRealX;

                if (pstMarquee->usRealX - ucGap < usExcess)
                {
                    pstMarquee->usStartPos = pstMarquee->usRealWidth - (pstMarquee->usRealX - ucGap);
                }
                pstMarquee->usRealX = 0;
            }
            else
            {
                pstMarquee->usStartPos = 0;
            }
        }
    }

    return HV_FALSE;
}

static VOID Hv_Drv_OsdFont_MarqueeScrollingLeftUpdateStep(OsdMarqueeNode *pstMarquee)
{
    OsdComponentNode *pstNode = pstMarquee->stIterator.pstComponent;
    UCHAR8 ucStep = HV_OSD_CONFIG_MARQUEE_STEP;

    if (pstMarquee->usRealX > ucStep)
    {
        pstMarquee->usRealX -= ucStep;
        pstMarquee->usStartPos = 0;
    }
    else
    {
        if (pstMarquee->usRealWidth > pstNode->stBox.usW)
        {
            pstMarquee->usStartPos += ucStep - pstMarquee->usRealX;
            OSD_DRV_LOG_DEBUG("startPos: %d diff between %d and %d: %d", pstMarquee->usStartPos, pstMarquee->usRealWidth, pstNode->stBox.usW,
                    pstMarquee->usRealWidth - pstNode->stBox.usW);
            if (pstMarquee->usStartPos >= pstMarquee->usRealWidth - pstNode->stBox.usW)
            {
                pstMarquee->usStartPos = pstMarquee->usRealWidth - pstNode->stBox.usW;
                /* reverse direction */
                *(OsdMarqueeDirection*)&pstMarquee->pvMarqueeArgs = OSD_MARQUEE_RIGHT;
            }
        }
        else
        {
            pstMarquee->usStartPos = 0;
            /* reverse direction */
            *(OsdMarqueeDirection*)&pstMarquee->pvMarqueeArgs = OSD_MARQUEE_RIGHT;
        }
        pstMarquee->usRealX = 0;
    }

    return;
}

static VOID Hv_Drv_OsdFont_MarqueeScrollingRightUpdateStep(OsdMarqueeNode *pstMarquee)
{
    OsdComponentNode *pstNode = pstMarquee->stIterator.pstComponent;
    UCHAR8 ucStep = HV_OSD_CONFIG_MARQUEE_STEP;

    if (pstMarquee->usStartPos > ucStep)
    {
        pstMarquee->usStartPos -= ucStep;
    }
    else
    {
        pstMarquee->usStartPos = 0;
        if (pstMarquee->usRealWidth >= pstNode->stBox.usW)
        {
            /* reverse direction */
            *(OsdMarqueeDirection*)&pstMarquee->pvMarqueeArgs = OSD_MARQUEE_LEFT;
        }
        else
        {
            pstMarquee->usRealX += ucStep - pstMarquee->usStartPos;
            if (pstMarquee->usRealX >= pstNode->stBox.usW - pstMarquee->usRealWidth)
            {
                /* reverse direction */
                pstMarquee->usRealX = pstNode->stBox.usW - pstMarquee->usRealWidth;
                *(OsdMarqueeDirection*)&pstMarquee->pvMarqueeArgs = OSD_MARQUEE_LEFT;
            }
        }
    }

    return;
}

static BOOL Hv_Drv_OsdFont_MarqueeScrollingUpdateStep(OsdMarqueeNode *pstMarquee)
{
    if (OSD_MARQUEE_LEFT == pstMarquee->pvMarqueeArgs)
    {
        Hv_Drv_OsdFont_MarqueeScrollingLeftUpdateStep(pstMarquee);
    }
    else
    {
        Hv_Drv_OsdFont_MarqueeScrollingRightUpdateStep(pstMarquee);
    }

    return HV_FALSE;
}

static BOOL Hv_Drv_OsdFont_MarqueeSlidingLeftUpdateStep(OsdMarqueeNode *pstMarquee)
{
    OsdComponentNode *pstNode = pstMarquee->stIterator.pstComponent;
    OsdMarqueeSlidingParam *pstParam = NULL;
    UCHAR8 ucStep = HV_OSD_CONFIG_MARQUEE_STEP;
    USHORT16 usDestX = 0;
    USHORT16 usDestStart = 0;
    BOOL bMarqueeDone = HV_FALSE;

    pstParam = (OsdMarqueeSlidingParam*)&pstMarquee->pvMarqueeArgs;
    usDestX = pstParam->usDestRealX;
    usDestStart = pstParam->usDestStartPos;

    if (pstMarquee->usRealX > usDestX + ucStep)
    {
        pstMarquee->usRealX -= ucStep;
        pstMarquee->usStartPos = 0;
    }
    else
    {
        pstMarquee->usStartPos += ucStep - (pstMarquee->usRealX - usDestX);
        pstMarquee->usRealX = usDestX;

        if (pstMarquee->usStartPos >= usDestStart)
        {
            /* Marquee Done */
            pstMarquee->usStartPos = usDestStart;
            pstMarquee->enState = OSD_MARQUEE_STAT_PAUSED;
            bMarqueeDone = HV_TRUE;
        }
    }

    return bMarqueeDone;
}

static BOOL Hv_Drv_OsdFont_MarqueeSlidingRightUpdateStep(OsdMarqueeNode *pstMarquee)
{
    OsdComponentNode *pstNode = pstMarquee->stIterator.pstComponent;
    OsdMarqueeSlidingParam *pstParam = NULL;
    UCHAR8 ucStep = HV_OSD_CONFIG_MARQUEE_STEP;
    USHORT16 usDestX = 0;
    USHORT16 usDestStart = 0;
    BOOL bMarqueeDone = HV_FALSE;

    pstParam = (OsdMarqueeSlidingParam*)&pstMarquee->pvMarqueeArgs;
    usDestX = pstParam->usDestRealX;
    usDestStart = pstParam->usDestStartPos;

    if (pstMarquee->usStartPos > usDestStart + ucStep)
    {
        pstMarquee->usStartPos -= ucStep;
    }
    else
    {
        pstMarquee->usRealX += ucStep - (pstMarquee->usStartPos - usDestStart);
        pstMarquee->usStartPos = usDestStart;

        if (pstMarquee->usRealX >= usDestX)
        {
            pstMarquee->usRealX = usDestX;
            pstMarquee->enState = OSD_MARQUEE_STAT_PAUSED;
            bMarqueeDone = HV_TRUE;
        }
    }

    return bMarqueeDone;
}

VOID Hv_Drv_OsdFont_MarqueeSlidingNextUpdateComponent(OsdComponentNode *pstNode)
{
    OsdTextList *pstTextList = NULL;
    OsdNumber *pstNumber = NULL;
    OsdMarqueeNode *pstMarquee = pstNode->pstMarquee;
    OsdMarqueeSlidingNextParam *pstParam = pstMarquee->pvMarqueeArgs;

    switch (pstNode->stCp.enType)
    {
        case OSD_CP_TYPE_TEXT_LIST:
        {
            pstTextList = (OsdTextList*)Hv_Drv_OsdFont_GetComponentData(pstNode);
            pstTextList->ucStringCurID = pstParam->iNextValue;
            OSD_DRV_LOG_DEBUG("Marquee Sliding next text list ID: %d", pstTextList->ucStringCurID);
            break;
        }
        case OSD_CP_TYPE_NUMBER:
        {
            pstNumber = (OsdNumber*)Hv_Drv_OsdFont_GetComponentData(pstNode);
            pstNumber->iValue = pstParam->iNextValue;
            OSD_DRV_LOG_DEBUG("Marquee Sliding next Number: %d", pstNumber->iValue);
            break;
        }
        default:
        {
            break;
        }
    }
}

static BOOL Hv_Drv_OsdFont_MarqueeSlidingLeftNextUpdateStep(OsdMarqueeNode *pstMarquee)
{
    OsdComponentNode *pstNode = pstMarquee->stIterator.pstComponent;
    UCHAR8 ucStep = HV_OSD_CONFIG_MARQUEE_STEP;
    USHORT16 usX = 0;
    OsdMarqueeSlidingNextParam *pstParam = pstMarquee->pvMarqueeArgs;

    /* marquee out first part */
    if (OSD_MARQUEE_PART_FIRST == pstParam->ucPart)
    {
        if (pstMarquee->usRealX > ucStep)
        {
            pstMarquee->usRealX -= ucStep;
            pstMarquee->usStartPos = 0;
        }
        else
        {
            pstMarquee->usStartPos += ucStep - pstMarquee->usRealX;
            if (pstMarquee->usStartPos >= pstMarquee->usRealWidth)
            {
                Hv_Drv_OsdFont_MarqueeSlidingNextUpdateComponent(pstNode);

                /* recalculate width */
                Hv_Drv_OsdFont_CalculateComponentBox(pstNode, HV_TRUE);

                usX = Hv_Drv_OsdFont_GetComponentX(pstNode);
                /* record dest X */
                pstParam->usDestX = pstMarquee->usRealX - usX;

                if (ucStep > pstNode->stBox.usW)
                {
                    pstMarquee->usRealX = 0;
                }
                else
                {
                    pstMarquee->usRealX = pstNode->stBox.usW - ucStep;
                }
                pstMarquee->usStartPos = 0;

                /* switch to second part */
                pstParam->ucPart = OSD_MARQUEE_PART_SECOND;
            }
            else
            {
                pstMarquee->usRealX = 0;
            }
        }
    }
    else /* marquee in second part */
    {
        if (pstMarquee->usRealX > pstParam->usDestX + ucStep)
        {
            pstMarquee->usRealX -= ucStep;
        }
        else
        {
            pstMarquee->usRealX = pstParam->usDestX;

            /*TODO pause marquee until next menu operation */
            pstMarquee->enState = OSD_MARQUEE_STAT_PAUSED;
            OSD_DRV_LOG_DEBUG("Sliding left next: set componnet %d-%d state to PAUSED", pstNode->stCp.enType, pstNode->stCp.usIndex);

            /* switch as first part */
            pstParam->ucPart = OSD_MARQUEE_PART_FIRST;
        }
    }

    return HV_FALSE;
}

static BOOL Hv_Drv_OsdFont_MarqueeSlidingRightNextUpdateStep(OsdMarqueeNode *pstMarquee)
{
    OsdComponentNode *pstNode = pstMarquee->stIterator.pstComponent;
    UCHAR8 ucStep = HV_OSD_CONFIG_MARQUEE_STEP;
    USHORT16 usX = 0;
    OsdMarqueeSlidingNextParam *pstParam = pstMarquee->pvMarqueeArgs;

    /* marquee out first part */
    if (OSD_MARQUEE_PART_FIRST == pstParam->ucPart)
    {
        pstMarquee->usRealX += ucStep;
        if (pstMarquee->usRealX >= pstNode->stBox.usW)
        {
            pstMarquee->usStartPos = 0;

            Hv_Drv_OsdFont_MarqueeSlidingNextUpdateComponent(pstNode);

            /* recalculate width */
            Hv_Drv_OsdFont_CalculateComponentBox(pstNode, HV_TRUE);

            usX = Hv_Drv_OsdFont_GetComponentX(pstNode);
            /* record dest X */
            pstParam->usDestX = pstMarquee->usRealX - usX;

            pstMarquee->usRealX = 0;
            if (pstMarquee->usRealWidth >= ucStep)
            {
                pstMarquee->usStartPos = pstMarquee->usRealWidth - ucStep;
            }
            else
            {
                pstMarquee->usStartPos = 0;
                pstMarquee->usRealX += ucStep - pstMarquee->usRealWidth;
            }

            /* switch to second part */
            pstParam->ucPart = OSD_MARQUEE_PART_SECOND;
        }
    }
    else /* marquee in second part */
    {
        if (pstMarquee->usStartPos >= ucStep)
        {
            pstMarquee->usStartPos -= ucStep;
        }
        else
        {
            pstMarquee->usRealX += ucStep - pstMarquee->usStartPos;
            pstMarquee->usStartPos = 0;

            if (pstMarquee->usRealX >= pstParam->usDestX)
            {
                pstMarquee->usRealX = pstParam->usDestX;

                /* pause marquee until next menu operation */
                pstMarquee->enState = OSD_MARQUEE_STAT_PAUSED;
                OSD_DRV_LOG_DEBUG("Sliding right next: set componnet %d-%d state to PAUSED", pstNode->stCp.enType, pstNode->stCp.usIndex);

                /* switch as first part */
                pstParam->ucPart = OSD_MARQUEE_PART_FIRST;
            }
        }
    }

    return HV_FALSE;
}

BOOL Hv_Drv_OsdFont_MarqueeSlidingNextUpdateStep(OsdMarqueeNode *pstMarquee)
{
    OsdMarqueeSlidingNextParam *pstParam = pstMarquee->pvMarqueeArgs;

    if (OSD_MARQUEE_LEFT == pstParam->ucDirection)
    {
        return Hv_Drv_OsdFont_MarqueeSlidingLeftNextUpdateStep(pstMarquee);
    }
    else
    {
        return Hv_Drv_OsdFont_MarqueeSlidingRightNextUpdateStep(pstMarquee);
    }
}

BOOL Hv_Drv_OsdFont_MarqueeNodeStep(OsdMarqueeNode *pstMarquee, BOOL bStepForward)
{
    UCHAR8 ucStep = 0;
    UCHAR8 ucWindowIndex = 0;
    USHORT16 usX = 0;
    BOOL bMarqueeDone = HV_FALSE;
    OsdComponentNode *pstNode = pstMarquee->stIterator.pstComponent;
    OsdMarqueeType enMarqueeType = 0;

    /* integrity check */
    HV_ASSERT_TRUE_RET(pstNode->pstMarquee == pstMarquee, HV_FALSE);

    enMarqueeType = Hv_Drv_OsdFont_GetMarqueeType(pstNode);
    usX = Hv_Drv_OsdFont_GetComponentX(pstNode);

    //ucWindowIndex = Hv_Drv_OsdFont_GetComponentWindowIndex(pstNode);
    //Hv_Drv_OsdWindow_SetCurrentWindow(ucWindowIndex);

    /* execute marquee step */
    if (bStepForward)
    {
        ucStep = HV_OSD_CONFIG_MARQUEE_STEP;
        pstMarquee->usRealX -= usX;
        switch (enMarqueeType)
        {
            case OSD_MARQUEE_TYPE_CYCLING_LEFT:
            {
                bMarqueeDone = Hv_Drv_OsdFont_MarqueeCyclingLeftUpdateStep(pstMarquee);
                break;
            }
            case OSD_MARQUEE_TYPE_CYCLING_RIGHT:
            {
                bMarqueeDone = Hv_Drv_OsdFont_MarqueeCyclingRightUpdateStep(pstMarquee);
                break;
            }
            case OSD_MARQUEE_TYPE_SCROLLING_LEFT:
            case OSD_MARQUEE_TYPE_SCROLLING_RIGHT:
            {
                bMarqueeDone = Hv_Drv_OsdFont_MarqueeScrollingUpdateStep(pstMarquee);
                break;
            }
            case OSD_MARQUEE_TYPE_SLIDING_LEFT:
            {
                bMarqueeDone = Hv_Drv_OsdFont_MarqueeSlidingLeftUpdateStep(pstMarquee);
                break;
            }
            case OSD_MARQUEE_TYPE_SLIDING_RIGHT:
            {
                bMarqueeDone = Hv_Drv_OsdFont_MarqueeSlidingRightUpdateStep(pstMarquee);
                break;
            }
            case OSD_MARQUEE_TYPE_SLIDING_NEXT:
            {
                bMarqueeDone = Hv_Drv_OsdFont_MarqueeSlidingNextUpdateStep(pstMarquee);
                break;
            }
            default:
            {
                break;
            }
        }

        OSD_DRV_LOG_DEBUG("marquee step: realwidth: %d realX: %d startpos: %d",
                pstMarquee->usRealWidth, pstMarquee->usRealX, pstMarquee->usStartPos);

        pstMarquee->usRealX += usX;
    }

    /* draws later */
    Hv_Drv_OsdShift_InitTransaction(pstMarquee->pstRowInfo, HV_FALSE, HV_FALSE);
    //Hv_Drv_OsdShift_SetReplaceEnable(pstMarquee->pstColumn, pstMarquee->bMallocSlots ? HV_FALSE : HV_TRUE);

    Hv_Drv_OsdFont_DrawMarqueeText(pstMarquee, HV_FALSE);

    //pstMarquee->bMallocSlots = HV_FALSE;

    return bMarqueeDone;
}

VOID Hv_Drv_OsdFont_MarqueeStep(VOID)
{
    struct LinkedList *pstNode = NULL;
    struct LinkedList *pstNext = NULL;
    OsdMarqueeNode *pstMarquee = NULL;
    BOOL bMarqueeDone = HV_FALSE;

    Hv_Comm_LinkedList_ForEachSafe(&s_pstFontParam->stMarqueeHead, pstNext, pstNode)
    {
        pstMarquee = Hv_Comm_LinkedList_Entry(pstNode, OsdMarqueeNode, stEntry);

        if (OSD_MARQUEE_STAT_PAUSED == pstMarquee->enState)
        {
            continue;
        }

        bMarqueeDone = Hv_Drv_OsdFont_MarqueeNodeStep(pstMarquee, HV_TRUE);
        if (bMarqueeDone)
        {
            //Hv_Drv_OsdFont_RemoveMarqueeNode(pstMarquee);
        }
    }

    Hv_Drv_OsdResource_SetChangeRange(OSD_SRAM_CHANGE_FULL, 0, 0);

#ifdef MARQUEE_TEST
    Hv_Drv_OsdFrame_DrawPost();
#endif
    return;
}

#endif

#if (HV_PROJECT_CONFIG_OSD_TEXT_WRAPPING == HV_CONFIG_ON)

BOOL Hv_Drv_OsdFont_IsWrapText(OsdComponentNode *pstComponent)
{
    OsdText *pstText = NULL;
    OsdAsciiString *pstAscii = NULL;

    switch (pstComponent->stCp.enType)
    {
        case OSD_CP_TYPE_TEXT:
        {
            pstText = (OsdText*)(Hv_Drv_OsdFont_GetComponentData(pstComponent));

            return pstText->usIsLongText;
        }
        case OSD_CP_TYPE_ASCII_STRING:
        {
            pstAscii = (OsdAsciiString*)(Hv_Drv_OsdFont_GetComponentData(pstComponent));

            return pstAscii->usIsLongText;
        }
        default:
        {
            break;
        }
    }

    return HV_FALSE;
}

OsdWrapNode *Hv_Drv_OsdFont_AllocWrapNode(OsdComponentNode *pstComponent)
{
    OsdWrapNode *pstWrap = NULL;
    UCHAR8 ucMaxRows = 0;
    USHORT16 usMallocSize = 0;
    Status uiStat = HV_FAILURE;

    ucMaxRows = Hv_Drv_OsdFont_GetComponentRows(pstComponent);
    usMallocSize = sizeof(OsdWrapNode) + (USHORT16)ucMaxRows * sizeof(OsdRowInfo*);
    pstWrap = HV_MALLOC(usMallocSize);
    if (NULL == pstWrap)
    {
        OSD_DRV_LOG_ERROR("Osd failed to malloc wrap node for component %s, not enough memory %d",
                CP_NAME(pstComponent->stCp), usMallocSize);
        return NULL;
    }
    HV_MEMSET(pstWrap, 0, usMallocSize);
    Hv_Comm_LinkedList_Init(&pstWrap->stEntry);

    pstWrap->ucCurRow = 0;

    uiStat = Hv_Drv_OsdFont_IterInit(&pstWrap->stIterator, pstComponent);
    if (HV_FAILURE == uiStat)
    {
        HV_FREE(pstWrap);
        return NULL;
    }

    /* add to global wrap list */
    Hv_Comm_LinkedList_AddTail(&s_pstFontParam->stAllWrapNodes, &pstWrap->stEntry);

    return pstWrap;
}

VOID Hv_Drv_OsdFont_RemoveWrapNode(OsdWrapNode *pstWrap)
{
    if (NULL == pstWrap)
    {
        return;
    }

    /* remove from list */
    Hv_Comm_LinkedList_Del(&s_pstFontParam->stAllWrapNodes, &pstWrap->stEntry);

    /* free iterator */
    Hv_Drv_OsdFont_IterFree(&pstWrap->stIterator);

    /* free wrap node */
    HV_FREE(pstWrap);
    return;
}

VOID Hv_Drv_OsdFont_RemoveAllWrapNodes(VOID)
{
    struct LinkedList *pstNode = NULL;
    struct LinkedList *pstNext = NULL;
    OsdWrapNode *pstWrap = NULL;

    Hv_Comm_LinkedList_ForEachSafe(&s_pstFontParam->stAllWrapNodes, pstNext, pstNode)
    {
        pstWrap = Hv_Comm_LinkedList_Entry(pstNode, OsdWrapNode, stEntry);

        Hv_Drv_OsdFont_RemoveWrapNode(pstWrap);
    }
}

OsdWrapNode *Hv_Drv_OsdFont_FindWrapNode(OsdComponentNode *pstComponent, BOOL bInsert)
{
    struct LinkedList *pstNode = NULL;
    OsdWrapNode *pstWrap = NULL;

    Hv_Comm_LinkedList_ForEach(&s_pstFontParam->stAllWrapNodes, pstNode)
    {
        pstWrap = Hv_Comm_LinkedList_Entry(pstNode, OsdWrapNode, stEntry);

        if (pstWrap->stIterator.pstComponent == pstComponent)
        {
            return pstWrap;
        }
    }

    if (bInsert)
    {
        return Hv_Drv_OsdFont_AllocWrapNode(pstComponent);
    }

    return NULL;
}

static OsdIndexColumn* Hv_Drv_OsdFont_DrawWrapText(OsdRowInfo *pstRowInfo,
                            OsdComponentNode *pstComponent, OsdIndexColumn *pstCurColumn,
                            BOOL bIsUpdate)
{
    OsdWrapNode *pstWrap = NULL;
    OsdIndexColumn *pstRowStart = NULL;
    OsdIndexColumn *pstWordStart = NULL;
    OsdIndexColumn *pstColumnIndex = NULL;
    const UCHAR8 *pucMatrixAddr = NULL;
    USHORT16 usOffset = 0;
    USHORT16 usSpaceLeft = 0;
    USHORT16 usBackupSpaceLeft = 0;
    USHORT16 usBackupSlotIndex = 0;
    UCHAR8 *pucBackShiftBuffer = NULL;
    UCHAR8 ucBackupShiftWidth = 0;
    USHORT16 usBytes = 0;
    USHORT16 usCode = 0;
    USHORT16 usSlot = 0;
    UCHAR8 ucMaxRows = 0;
    UCHAR8 ucWidth = 0;
    UCHAR8 ucRealWidth = 0;
    UCHAR8 ucColorIndex = 0;
    OsdComponentIterator *pstIterator = NULL;
    OsdComponentIterator stBackupIterator = {0};
    BOOL bReadOut = HV_FALSE;

    pstWrap = Hv_Drv_OsdFont_FindWrapNode(pstComponent, HV_TRUE);
    if (NULL == pstWrap)
    {
        goto out;
    }
    pstIterator = &pstWrap->stIterator;

    ucMaxRows = Hv_Drv_OsdFont_GetComponentRows(pstComponent);
    if (pstWrap->ucCurRow >= ucMaxRows)
    {
        OSD_DRV_LOG_ERROR("Current row %d exceeds max rows %d", pstWrap->ucCurRow, ucMaxRows);
        goto out;
    }

    /* record drawn rows */
    pstWrap->apDrawnRows[pstWrap->ucCurRow] = pstRowInfo;

    usBytes = Hv_Drv_OsdCommon_GetResourceBytes(pstRowInfo->enSize, pstRowInfo->enType);
    pucBackShiftBuffer = HV_MALLOC(usBytes);
    if (NULL == pucBackShiftBuffer)
    {
        OSD_DRV_LOG_ERROR("Failed to draw wrap text %s, not enough memory: %d", CP_NAME(pstComponent->stCp), usBytes);
        goto out;
    }

    Hv_Drv_OsdResource_GetSize(pstRowInfo->enSize, &ucWidth, NULL);

    ucColorIndex = Hv_Drv_OsdFont_GetComponentColorIndex(pstComponent);

    pstWordStart = pstRowStart = pstCurColumn;
    usBackupSpaceLeft = usSpaceLeft = pstComponent->stBox.usW;
    Hv_Drv_OsdFont_IterCopy(&stBackupIterator, pstIterator);
    while (!Hv_Drv_OsdFont_IterIsEnd(pstIterator))
    {
        usCode = Hv_Drv_OsdFont_IterGetCode(pstIterator);

        /* preserve state at word start */
        if (__WORDSPLIT__ == usCode)
        {
            /* record next word start */
            Hv_Drv_OsdFont_IterStepForward(pstIterator);
#if (HV_OSD_CONFIG_WRAP_SPLIT_WORD == HV_CONFIG_OFF)
            Hv_Drv_OsdFont_IterCopy(&stBackupIterator, pstIterator);

            /* preserve shift status */
            Hv_Drv_OsdShift_Preserve(pucBackShiftBuffer, &ucBackupShiftWidth);

            pstWordStart = pstCurColumn;

            usBackupSpaceLeft = usSpaceLeft;

            /* preserve resource */
            if (!bIsUpdate)
            {
                usBackupSlotIndex = pstRowInfo->pstLoadedBank->usCurrentSlots;
            }

            OSD_DRV_LOG_DEBUG("skip __WORDSPLIT__, preserve current state. shift width: %d column: 0x%x space left: %d",
                    ucBackupShiftWidth, pstWordStart, usBackupSpaceLeft);
#else
            OSD_DRV_LOG_DEBUG("skip __WORDSPLIT__, no need to preserve current state.");
#endif
            continue;
        }

        Hv_Drv_OsdFont_IterGetCurrentCharacter(pstIterator, &pucMatrixAddr, &usOffset, &ucRealWidth);
        if ((__NEWLINE__ == usCode) || (usSpaceLeft < ucRealWidth))
        {
            if (usSpaceLeft < ucRealWidth)
            {
#if (HV_OSD_CONFIG_WRAP_SPLIT_WORD == HV_CONFIG_OFF)
                OSD_DRV_LOG_DEBUG("Space left %d, width need %d, rewind to last word", usSpaceLeft, ucRealWidth);

                /* do not rewind if there is only one word */
                if (pstWordStart == pstRowStart)
                {
                    OSD_DRV_LOG_DEBUG("word excess BOX width, do not rewind");
                }
                else
                {
                    /* restore resource */
                    if (bIsUpdate)
                    {
                        Hv_Drv_OsdFont_FreeRangedRowSlots(pstRowInfo, pstWordStart, pstCurColumn, HV_TRUE);
                    }
                    else
                    {
                        pstRowInfo->pstLoadedBank->usCurrentSlots = usBackupSlotIndex;
                    }

                    /* restore iterator */
                    Hv_Drv_OsdFont_IterCopy(pstIterator, &stBackupIterator);

                    /* restore shift status */
                    Hv_Drv_OsdShift_Restore(pucBackShiftBuffer, ucBackupShiftWidth);

                    /* restore current column */
                    pstCurColumn = pstWordStart;

                    /* restore remaining space */
                    usSpaceLeft = usBackupSpaceLeft;
                }
#else
                OSD_DRV_LOG_DEBUG("Space left %d, width need %d, do not rewind", usSpaceLeft, ucRealWidth);
#endif
            }
            else
            {
                OSD_DRV_LOG_DEBUG("draw __NEWLINE__");

                /* skip __NEWLINE__ */
                Hv_Drv_OsdFont_IterStepForward(pstIterator);
            }

            break;
        }

        /* begin to shift */
        bReadOut = Hv_Drv_OsdShift_WriteCharacter(pstRowInfo, pucMatrixAddr, usOffset, ucRealWidth);

        if (HV_TRUE == bReadOut)
        {
            usSlot = Hv_Drv_OsdFont_TakeCurrentSlot(pstRowInfo, bIsUpdate);
            Hv_Cal_OsdIndex_SetColIndex(pstCurColumn, usSlot);
            Hv_Cal_OsdIndex_SetColType(pstCurColumn, OSD_COLUMN_FONT);
            Hv_Cal_OsdIndex_SetFontColColorIndex(pstCurColumn, ucColorIndex);

            OSD_DRV_LOG_DEBUG("Draw Wrap Text column 0x%x index %u colorIndex %d delta %d",
                    pstCurColumn, Hv_Cal_OsdIndex_GetColIndex(pstCurColumn), ucColorIndex,
                    Hv_Cal_OsdIndex_GetColDelta(pstCurColumn));

            pstCurColumn++;
        }

        usSpaceLeft -= ucRealWidth;

        /* read in next char */
        Hv_Drv_OsdFont_IterStepForward(pstIterator);
    }

    /*
     * Conditions at this point:
     * 1. got a NEWLINE character
     * 2. word excess box, and rewinded
     * 3. word excess box, but can't rewind (only one word)
     * 4. word excess box, no need to rewind (HV_OSD_CONFIG_WRAP_SPLIT_WORD == ON)
     * 5. end of iterator
     *
     * In all cases, we need to fill blank pixels
     */
    ucRealWidth = Hv_Drv_OsdShift_EndTransaction(HV_TRUE);
    if (0 != ucRealWidth)
    {
        usSlot = Hv_Drv_OsdFont_TakeCurrentSlot(pstRowInfo, bIsUpdate);
        Hv_Cal_OsdIndex_SetColIndex(pstCurColumn, usSlot);
        Hv_Cal_OsdIndex_SetColType(pstCurColumn, OSD_COLUMN_FONT);
        Hv_Cal_OsdIndex_SetFontColColorIndex(pstCurColumn, ucColorIndex);

        /* set delta */
        if (ucRealWidth + usSpaceLeft < ucWidth)
        {
            Hv_Cal_OsdIndex_SetColDelta(pstCurColumn, HV_FALSE, ucWidth - (usSpaceLeft + ucRealWidth));
            usSpaceLeft = 0;
        }
        else
        {
            Hv_Cal_OsdIndex_SetColDelta(pstCurColumn, HV_FALSE, 0);
            usSpaceLeft -= (ucWidth - ucRealWidth);
        }

        OSD_DRV_LOG_DEBUG("Draw Text column 0x%x index %u colorIndex %d delta %d",
                pstCurColumn, Hv_Cal_OsdIndex_GetColIndex(pstCurColumn), ucColorIndex,
                Hv_Cal_OsdIndex_GetColDelta(pstCurColumn));

        pstCurColumn++;
    }

    /* fill remaining blanks */
    pstCurColumn = Hv_Drv_OsdIndex_FillColumnBlanks(pstCurColumn, usSpaceLeft, ucWidth);

    ++pstWrap->ucCurRow;

    /* free backup iterator */
    Hv_Drv_OsdFont_IterFree(&stBackupIterator);

    /* free backup shift buffer */
    HV_FREE(pucBackShiftBuffer);

out:
    return pstCurColumn;
}
#endif

#if (HV_OSD_CONFIG_RESERVE_SLOTS_BYROW == HV_CONFIG_ON)
static VOID Hv_Drv_OsdFont_ReInitRowCatalogs(OsdPhyFontInfo *pstFontInfo)
{
    s_pstFontParam->ucCatalogCount = 0;
}

#else

USHORT16 Hv_Drv_OsdFont_GetExpandSlots(OsdUpdateManager *pstManager, USHORT16 usRequireSlots)
{
    UINT32 uiRemainSize = 0;
    USHORT16 usRequiredSize = 0;
    USHORT16 usResourceOffset = 0;
    USHORT16 usRegionLimit = 0;
    USHORT16 usLimitSlots = 0;
    USHORT16 usCurrentSlots = 0;
    USHORT16 usMinimumSlots = 0;
    USHORT16 usBytes = 0;
    USHORT16 usSlots = 0;
    OsdRowCatalog *pstCatalog = pstManager->pstCatalog;

    usSlots = Hv_Drv_OsdRadix_GetEmptySlots(pstManager->pstUpdateSlotsManager);
    usMinimumSlots = usRequireSlots - usSlots;

    usBytes = Hv_Drv_OsdCommon_GetResourceBytes(pstCatalog->enSize, pstCatalog->enType);
    usRequiredSize = usBytes * usMinimumSlots;
    usResourceOffset = Hv_Drv_OsdResource_GetCurrentOffset();
    uiRemainSize = OSD_RESOURCE_BUFFER_SIZE - usResourceOffset;

    OSD_DRV_LOG_DEBUG("Calculate slots number for update region expansion. catalog(%s %s),"
                "update requires %d slots, current %d empty slots, %d bytes available",
                Hv_Drv_OsdCommon_GetResourceSizeStr(pstCatalog->enSize),
                Hv_Drv_OsdCommon_GetResourceTypeStr(pstCatalog->enType),
                usRequireSlots, usSlots, uiRemainSize);

    if (uiRemainSize < usRequiredSize)
    {
        OSD_DRV_LOG_ERROR("osd fatal error. resource buffer not enough for update,"
                " minimum requires %d bytes", usRequiredSize);
        return 0;
    }

    usCurrentSlots = Hv_Drv_OsdRadix_GetTotalSlots(pstManager->pstUpdateSlotsManager);
    if (usCurrentSlots + usMinimumSlots > OSD_BANK_CAPACITY)
    {
        OSD_DRV_LOG_ERROR("osd fatal error. update region current has %d slots, need minimum %d slots. "
                "would exceed OSD_BANK_CAPACITY", usCurrentSlots, usMinimumSlots);
        return 0;
    }

    /* decide appropriate slots to expand */
    usRegionLimit = HV_OSD_CONFIG_EXPAND_REGION_MAX;
    if (usRegionLimit > uiRemainSize)
    {
        OSD_DRV_LOG_DEBUG("Resource buffer nearly exhausted, only %d bytes available", uiRemainSize);
        usRegionLimit = uiRemainSize;
    }

    /* mostlikely case */
    usLimitSlots = HV_OSD_CONFIG_SLOTS_EXPAND;
    usSlots = max(usRequireSlots, usLimitSlots);

    /* region expanded may not excess region limit */
    if (usSlots * usBytes < usRegionLimit)
    {
        OSD_DRV_LOG_DEBUG("Optimal solution: Expand update region by %d slots", usSlots);
        goto out;
    }

    usSlots = min(usRequireSlots, usLimitSlots);
    if (usSlots * usBytes < usRegionLimit)
    {
        usSlots = (usSlots < usMinimumSlots ? usMinimumSlots : usSlots);
        OSD_DRV_LOG_DEBUG("Suboptimal solution: Expand update region by %d slots", usSlots);
        goto out;
    }

    /* last try */
    usLimitSlots = usRegionLimit / usBytes;
    usSlots = max(usMinimumSlots, usLimitSlots);
    OSD_DRV_LOG_DEBUG("Least optimal solution: Expand update region by %d slots", usSlots);
out:
    /* should not exceed OSD_BANK_CAPACITY */
    if (usSlots + usCurrentSlots > OSD_BANK_CAPACITY)
    {
        OSD_DRV_LOG_DEBUG("Current slots: %d, Expand %d slots would exceed OSD_BANK_CAPACITY, "
                "expand %d instead", usCurrentSlots, usSlots, OSD_BANK_CAPACITY - usCurrentSlots);

        usSlots = OSD_BANK_CAPACITY - usCurrentSlots;
    }
    return usSlots;
}

VOID Hv_Drv_OsdFont_MoveUpdateRegion(OsdUpdateManager *pstManager, USHORT16 usOffset)
{
    OsdWindowPhyId enSavedWindowId = 0;
    OsdRowCatalog *pstCatalog = pstManager->pstCatalog;
    OsdLoadedBank *pstLoadedBank = pstManager->pstUpdateBank;
    USHORT16 usOrigOffset = pstLoadedBank->usCurrentResourceOffset;
    UCHAR8 ucIndex = 0;

    pstLoadedBank->usCurrentResourceOffset += usOffset;
    OSD_DRV_LOG_DEBUG("move update region(window-%d %s %s) from 0x%x to 0x%x, size: %d",
            pstCatalog->ucWindowId,
            Hv_Drv_OsdCommon_GetResourceSizeStr(pstCatalog->enSize),
            Hv_Drv_OsdCommon_GetResourceTypeStr(pstCatalog->enType),
            usOrigOffset, pstLoadedBank->usCurrentResourceOffset,
            pstManager->usSize);

    enSavedWindowId = Hv_Drv_OsdWindow_GetCurrentWindow();
    Hv_Drv_OsdWindow_SetCurrentWindow(pstCatalog->ucWindowId);

    ucIndex = Hv_Drv_OsdResource_FindResource(pstLoadedBank->ucBankID, HV_FALSE);
    Hv_Cal_OsdWindow_SetResource(ucIndex, pstLoadedBank->usCurrentResourceOffset,
            pstCatalog->enSize);

    memmove(OSD_RESOURCE_DDR_ADDR(pstLoadedBank->usCurrentResourceOffset),
            OSD_RESOURCE_DDR_ADDR(usOrigOffset), pstManager->usSize);

    Hv_Drv_OsdWindow_SetCurrentWindow(enSavedWindowId);
}

VOID Hv_Drv_OsdFont_MoveFollowingUpdateRegions(OsdUpdateManager *pstManager, USHORT16 usOffset)
{
    OsdUpdateManager *pstCurManager = NULL;
    struct LinkedList *pstNode = NULL;

    Hv_Comm_LinkedList_ForEachR(&s_pstFontParam->stAllUpdateManagers, pstNode)
    {
        pstCurManager = Hv_Comm_LinkedList_Entry(pstNode, OsdUpdateManager, stEntry);
        if (pstCurManager == pstManager)
        {
            break;
        }

        Hv_Drv_OsdFont_MoveUpdateRegion(pstCurManager, usOffset);
    }
}

BOOL Hv_Drv_OsdFont_ExpandUpdateRegion(OsdUpdateManager *pstManager, USHORT16 usRequireSlots)
{
    USHORT16 usSlots = 0;
    USHORT16 usBytes = 0;
    USHORT16 usSizeNeed = 0;
    OsdRowCatalog *pstCatalog = pstManager->pstCatalog;

    usSlots = Hv_Drv_OsdFont_GetExpandSlots(pstManager, usRequireSlots);
    if (0 == usSlots)
    {
        /* Try to free slots? not that crazzzy */
        OSD_DRV_LOG_ERROR("lower the setting of HV_OSD_CONFIG_EXPAND_REGION_MAX and try again");
        return HV_FAILURE;
    }

    usBytes = Hv_Drv_OsdCommon_GetResourceBytes(pstCatalog->enSize, pstCatalog->enType);
    usSizeNeed = usBytes * usSlots;
    usSizeNeed = ALIGN(usSizeNeed, 16);

    if (NULL == pstManager->pstUpdateSlotsManager)
    {
        pstManager->pstUpdateSlotsManager = Hv_Drv_OsdRadix_BuildTree(usSlots);
        HV_ASSERT_TRUE_RET(pstManager->pstUpdateSlotsManager != NULL, HV_FAILURE);
    }
    else
    {
        /* move following update regions */
        Hv_Drv_OsdFont_MoveFollowingUpdateRegions(pstManager, usSizeNeed);

        /* expand tree slots */
        pstManager->pstUpdateSlotsManager =
                Hv_Drv_OsdRadix_ExpandTree(pstManager->pstUpdateSlotsManager, usSlots);
        HV_ASSERT_TRUE_RET(pstManager->pstUpdateSlotsManager != NULL, HV_FAILURE);
    }

    pstManager->usSize += usSizeNeed;
    Hv_Drv_OsdResource_AllocRegion(usSizeNeed);

    return HV_SUCCESS;
}
static OsdUpdateManager* Hv_Drv_OsdFont_CreateUpdateManager(OsdRowCatalog *pstCatalog, USHORT16 usRequireSlots)
{
    OsdUpdateManager *pstManager = NULL;
    OsdPhyFontInfo *pstFontInfo = NULL;

    pstManager = (OsdUpdateManager*)HV_MALLOC(sizeof(*pstManager));
    HV_ASSERT_TRUE_RET(pstManager != NULL, NULL);
    HV_MEMSET(pstManager, 0, sizeof(*pstManager));

    pstManager->usResourceOffset = Hv_Drv_OsdResource_GetCurrentOffset();
    pstManager->pstCatalog = pstCatalog;
    /* create new loaded bank */
    pstManager->pstUpdateBank = Hv_Drv_OsdResource_NewLoadedBank(pstCatalog->enSize, pstCatalog->enType);
    /* insert update bank to window resource array */
    pstFontInfo = Hv_Drv_OsdFont_GetCurrentFontInfo();
    if (pstFontInfo->ucResourceCount >= OSD_WINDOW_MAX_RESOURCE_COUNT)
    {
        OSD_DRV_LOG_ERROR("osd fatal error. no available resource slot for update bank. "
                "try another update method: reserve slots by row");
        HV_FREE(pstManager);

        return NULL;
    }
    pstFontInfo->apResources[pstFontInfo->ucResourceCount] = pstManager->pstUpdateBank;
    Hv_Cal_OsdWindow_SetResource(pstFontInfo->ucResourceCount,
            pstManager->pstUpdateBank->usCurrentResourceOffset, pstCatalog->enSize);
    OSD_DRV_LOG_DEBUG("update bank %d inserted at %d", pstManager->pstUpdateBank->ucBankID,
            pstFontInfo->ucResourceCount);
    ++pstFontInfo->ucResourceCount;

    Hv_Drv_OsdFont_ExpandUpdateRegion(pstManager, usRequireSlots);

    Hv_Comm_LinkedList_Init(&pstManager->stEntry);
    Hv_Comm_LinkedList_AddTail(&s_pstFontParam->stAllUpdateManagers, &pstManager->stEntry);

    OSD_DRV_LOG_DEBUG("created update manager for catalog(%s %s), resource offset: 0x%x"
            " size: %d bytes, slots number: %d",
            Hv_Drv_OsdCommon_GetResourceSizeStr(pstCatalog->enSize),
            Hv_Drv_OsdCommon_GetResourceTypeStr(pstCatalog->enType),
            pstManager->pstUpdateBank->usCurrentResourceOffset,
            pstManager->usSize, pstManager->pstUpdateSlotsManager->usTotalSlots);

    return pstManager;
}

static VOID Hv_Drv_OsdFont_FreeUpdateManager(OsdUpdateManager *pstManager)
{
    HV_ASSERT_TRUE_VOID((pstManager != NULL) && (pstManager->pstUpdateSlotsManager != NULL));

    Hv_Drv_OsdRadix_DestroyTree(pstManager->pstUpdateSlotsManager);
    Hv_Comm_LinkedList_Del(&s_pstFontParam->stAllUpdateManagers, &pstManager->stEntry);

    HV_FREE(pstManager);
}

static VOID Hv_Drv_OsdFont_ReInitRowCatalogs(OsdPhyFontInfo *pstFontInfo)
{
    OsdRowCatalog *pstCatalog = pstFontInfo->pstCatalogs;
    OsdRowCatalog *pstNext = NULL;

    while (NULL != pstCatalog)
    {
        pstNext = pstCatalog->pstNext;

        if (NULL != pstCatalog->pstSlotsManager)
        {
            Hv_Drv_OsdRadix_DestroyTree(pstCatalog->pstSlotsManager);
        }
        if (NULL != pstCatalog->pstUpdateManager)
        {
            Hv_Drv_OsdFont_FreeUpdateManager(pstCatalog->pstUpdateManager);
        }
        HV_FREE(pstCatalog);

        pstCatalog = pstNext;
    }

    pstFontInfo->pstCatalogs = NULL;
}

VOID Hv_Drv_OsdFont_SetCurrentCatalog(OsdRowCatalog *pstCatalog)
{
    s_pstFontParam->pstCurrentCatalog = pstCatalog;
}

OsdRowCatalog* Hv_Drv_OsdFont_GetCurrentCatalog(VOID)
{
    return s_pstFontParam->pstCurrentCatalog;
}

#endif

VOID Hv_Drv_OsdFont_SetFontStart(USHORT16 usX, USHORT16 usY)
{
    OSD_DRV_LOG_DEBUG("font start position set to (%u, %u)", usX, usY);

    Hv_Cal_OsdWindow_SetFontStart(usX, usY);
}

VOID Hv_Drv_OsdFont_SetFontEnable(BOOL bEnable)
{
    Hv_Cal_OsdWindow_SetFontEnable(bEnable);
}

#if (HV_PROJECT_CONFIG_OSD_TEXT_WRAPPING == HV_CONFIG_ON)
UCHAR8 Hv_Drv_OsdFont_GetWrapSpace(OsdComponentNode *pstNode)
{
    OsdText *pstText = NULL;
    OsdAsciiString *pstAscii = NULL;

    switch (pstNode->stCp.enType)
    {
        case OSD_CP_TYPE_TEXT:
        {
            pstText = (OsdText*)(Hv_Drv_OsdFont_GetComponentData(pstNode));
            if (pstText->usIsLongText)
            {
                return pstText->ucWrapRowSpace;
            }
            else
            {
                return 0;
            }
        }
        case OSD_CP_TYPE_ASCII_STRING:
        {
            pstAscii = (OsdAsciiString*)(Hv_Drv_OsdFont_GetComponentData(pstNode));
            if (pstAscii->usIsLongText)
            {
                return pstAscii->ucWrapRowSpace;
            }
            else
            {
                return 0;
            }

        }
        default:
        {
            break;
        }
    }

    return 0;
}
#endif

static VOID Hv_Drv_OsdFont_RowInfoInsertComponent(OsdRowInfo *pstRowInfo,
                            OsdComponentNode *pstNode, BOOL bInsertMultiple)
{
    UCHAR8 ucIndex = 0;
    UCHAR8 ucTailIndex = 0;
    UCHAR8 ucHeight = 0;
    USHORT16 usX = 0;
    USHORT16 usXCompare = 0;
    USHORT16 usY = pstRowInfo->usY;
    UCHAR8 ucRowCount = 1;
    UCHAR8 ucRowIndex = 0;
    struct LinkedList *pstNextNode = NULL;
#if (HV_PROJECT_CONFIG_OSD_TEXT_WRAPPING == HV_CONFIG_ON)
    UCHAR8 ucWrapSpace = 0;

    ucWrapSpace = Hv_Drv_OsdFont_GetWrapSpace(pstNode);
#endif

    if (bInsertMultiple)
    {
        ucRowCount = Hv_Drv_OsdFont_GetComponentRows(pstNode);
    }

    if (NULL == pstNode->pstRowInfo)
    {
        pstNode->pstRowInfo = pstRowInfo;
    }

    Hv_Drv_OsdResource_GetSize(pstRowInfo->enSize, NULL, &ucHeight);

    usX = Hv_Drv_OsdFont_GetComponentX(pstNode);
    for (ucRowIndex = 0; ucRowIndex < ucRowCount; ++ucRowIndex)
    {
        HV_ASSERT_TRUE_VOID(pstRowInfo->ucComponentCount < OSD_ROW_MAX_COMPONENTS);
        //HV_ASSERT_TRUE_VOID(pstRowInfo->usY == usY + ucRowIndex * ucHeight);

        for (ucIndex = 0; ucIndex < pstRowInfo->ucComponentCount; ++ucIndex)
        {
            usXCompare =
                Hv_Drv_OsdFont_GetComponentX(pstRowInfo->apCurrentComponents[ucIndex]);
            if (usX < usXCompare)
            {
                break;
            }
        }

        for (ucTailIndex = pstRowInfo->ucComponentCount; ucTailIndex > ucIndex; --ucTailIndex)
        {
            pstRowInfo->apCurrentComponents[ucTailIndex] =
                pstRowInfo->apCurrentComponents[ucTailIndex - 1];
        }

#if (HV_OSD_CONFIG_RESERVE_SLOTS_BYROW == HV_CONFIG_OFF)
        pstRowInfo->ucResourceCount += pstNode->ucFontCount;
#endif

        pstRowInfo->apCurrentComponents[ucIndex] = pstNode;
        pstRowInfo->ucComponentCount++;

        OSD_DRV_LOG_DEBUG("row %u component(%s) insert at %u", ucRowIndex, CP_NAME(pstNode->stCp), ucIndex);

#if (HV_PROJECT_CONFIG_OSD_TEXT_WRAPPING == HV_CONFIG_ON)
        if (bInsertMultiple && Hv_Drv_OsdFont_IsWrapText(pstNode))
        {
            usY += ucHeight + ucWrapSpace;
            pstRowInfo = Hv_Drv_OsdFont_GetRowInfoByYaxis(usY, HV_FALSE);
            if (NULL == pstRowInfo)
            {
                OSD_DRV_LOG_ERROR("Could not insert %d-th row of component %s, row %d not found",
                        ucRowIndex + 1, CP_NAME(pstNode->stCp), usY);

                return;
            }
        }
        else
        {
#endif
            /* next row */
            pstNextNode = pstRowInfo->stEntry.next;
            pstRowInfo = Hv_Comm_LinkedList_Entry(pstNextNode, OsdRowInfo, stEntry);
#if (HV_PROJECT_CONFIG_OSD_TEXT_WRAPPING == HV_CONFIG_ON)
        }
#endif
    }
}

static VOID Hv_Drv_OsdFont_LogComponent(OsdComponentNode *pstNode)
{
    UCHAR8 ucBlockID = 0;
    OsdText *pstText = NULL;
    OsdTextList *pstTextList = NULL;
    OsdAsciiString *pstCustom = NULL;
    OsdNumber *pstNumber = NULL;
    OsdIcon *pstIcon = NULL;
    OsdIconList *pstIconList = NULL;
    OsdBmp *pstBMP = NULL;
    OsdBmpList *pstBmpList = NULL;

    switch (pstNode->stCp.enType)
    {
        case OSD_CP_TYPE_TEXT:
        {
            pstText = (OsdText*)Hv_Drv_OsdFont_GetComponentData(pstNode);
            ucBlockID = pstText->ucBlockID;

            OSD_DRV_LOG_DEBUG("Draw Text %u(block: %d) at (%u, %u) box width %u: Fontsize %s Bpp %s alignType %s",
                    pstText->usStringId, pstText->ucBlockID, pstText->usX, pstText->usY, pstText->usWidth,
                    Hv_Drv_OsdCommon_GetResourceSizeStr(g_astAllBlocks[ucBlockID].usResourceSize),
                    Hv_Drv_OsdCommon_GetResourceTypeStr(g_astAllBlocks[ucBlockID].ucResourceType),
                    Hv_Drv_OsdCommon_GetTextAlignTypeStr(pstText->usAlignType));
            break;
        }
        case OSD_CP_TYPE_TEXT_LIST:
        {
            pstTextList = (OsdTextList*)Hv_Drv_OsdFont_GetComponentData(pstNode);
            ucBlockID = pstTextList->ucBlockID;

            OSD_DRV_LOG_DEBUG("Draw Text List %u(block: %d) at (%u, %u) "
                    "box width %u: FontSize %s Bpp %s alignType %s",
                    pstTextList->pusStringIDList[pstTextList->ucStringCurID],
                    pstTextList->ucBlockID, pstTextList->usX,
                    pstTextList->usY, pstTextList->usWidth,
                    Hv_Drv_OsdCommon_GetResourceSizeStr(g_astAllBlocks[ucBlockID].usResourceSize),
                    Hv_Drv_OsdCommon_GetResourceTypeStr(g_astAllBlocks[ucBlockID].ucResourceType),
                    Hv_Drv_OsdCommon_GetTextAlignTypeStr(pstTextList->usAlignType));
            break;
        }
        case OSD_CP_TYPE_ASCII_STRING:
        {
            pstCustom = (OsdAsciiString*)Hv_Drv_OsdFont_GetComponentData(pstNode);
            ucBlockID = pstCustom->ucBlockID;

            OSD_DRV_LOG_DEBUG("Draw Custom String %s(block: %d) len %u at (%u, %u) "
                    "box width %u: FontSize %s Bpp %s alignType %s",
                    CP_NAME(pstNode->stCp), pstCustom->ucBlockID, pstCustom->ucStringLength,
                    pstCustom->usX, pstCustom->usY, pstCustom->usWidth,
                    Hv_Drv_OsdCommon_GetResourceSizeStr(g_astAllBlocks[ucBlockID].usResourceSize),
                    Hv_Drv_OsdCommon_GetResourceTypeStr(g_astAllBlocks[ucBlockID].ucResourceType),
                    Hv_Drv_OsdCommon_GetTextAlignTypeStr(pstCustom->usAlignType));

            break;
        }
        case OSD_CP_TYPE_NUMBER:
        {
            pstNumber = (OsdNumber*)Hv_Drv_OsdFont_GetComponentData(pstNode);
            ucBlockID = pstNumber->ucBlockID;

            OSD_DRV_LOG_DEBUG("Draw Number %d(block: %d) at (%u, %u) "
                    "box width %u: FontSize %s Bpp %s alignType %s",
                    pstNumber->iValue, pstNumber->ucBlockID, pstNumber->usX,
                    pstNumber->usY, pstNumber->usWidth,
                    Hv_Drv_OsdCommon_GetResourceSizeStr(g_astAllBlocks[ucBlockID].usResourceSize),
                    Hv_Drv_OsdCommon_GetResourceTypeStr(g_astAllBlocks[ucBlockID].ucResourceType),
                    Hv_Drv_OsdCommon_GetTextAlignTypeStr(pstNumber->usAlignType));
            break;
        }
        /* TODO Draw complicated number */
        case OSD_CP_TYPE_ICON:
        {
            pstIcon = (OsdIcon*)Hv_Drv_OsdFont_GetComponentData(pstNode);
            ucBlockID = pstIcon->ucBlockID;

            OSD_DRV_LOG_DEBUG("Draw Icon %u(Hcount: %d Vcount: %d) at (%u, %u) "
                    "FontSize %s Bpp %s", pstIcon->usIconOffset, pstIcon->ucHCount,
                    pstIcon->ucVCount, pstIcon->usX, pstIcon->usY,
                    Hv_Drv_OsdCommon_GetResourceSizeStr(g_astAllBlocks[ucBlockID].usResourceSize),
                    Hv_Drv_OsdCommon_GetResourceTypeStr(g_astAllBlocks[ucBlockID].ucResourceType));
            break;
        }
        case OSD_CP_TYPE_ICON_LIST:
        {
            pstIconList = (OsdIconList*)Hv_Drv_OsdFont_GetComponentData(pstNode);
            ucBlockID = pstIconList->ucBlockID;

            OSD_DRV_LOG_DEBUG("Draw Icon List %u(Hcount: %d Vcount: %d) at (%u, %u) "
                    "FontSize %s Bpp %s", pstIconList->pstIconList[pstIconList->ucIconCurID].usIconOffset,
                    pstIconList->ucHCount, pstIconList->ucVCount, pstIconList->usX, pstIconList->usY,
                    Hv_Drv_OsdCommon_GetResourceSizeStr(g_astAllBlocks[ucBlockID].usResourceSize),
                    Hv_Drv_OsdCommon_GetResourceTypeStr(g_astAllBlocks[ucBlockID].ucResourceType));
            break;
        }
        case OSD_CP_TYPE_BMP:
        {
            pstBMP = (OsdBmp*)Hv_Drv_OsdFont_GetComponentData(pstNode);
            ucBlockID = pstBMP->ucBlockID;

            OSD_DRV_LOG_DEBUG("Draw BMP %u(Hcount: %d Vcount: %d) at (%u, %u) "
                    "FontSize %s Bpp %s", pstBMP->usBMPOffset, pstBMP->ucHCount,
                    pstBMP->ucVCount, pstBMP->usX, pstBMP->usY,
                    Hv_Drv_OsdCommon_GetResourceSizeStr(g_astAllBlocks[ucBlockID].usResourceSize),
                    Hv_Drv_OsdCommon_GetResourceTypeStr(g_astAllBlocks[ucBlockID].ucResourceType));
            break;
        }
        case OSD_CP_TYPE_BMP_LIST:
        {
            pstBmpList = (OsdBmpList*)Hv_Drv_OsdFont_GetComponentData(pstNode);
            ucBlockID = pstBmpList->ucBlockID;

            OSD_DRV_LOG_DEBUG("Draw Bmp List %u(Hcount: %d Vcount: %d) at (%u, %u) "
                    "FontSize %s Bpp %s", pstBmpList->pstBmpList[pstBmpList->ucBmpCurID].usBMPOffset,
                    pstBmpList->ucHCount, pstBmpList->ucVCount, pstBmpList->usX, pstBmpList->usY,
                    Hv_Drv_OsdCommon_GetResourceSizeStr(g_astAllBlocks[ucBlockID].usResourceSize),
                    Hv_Drv_OsdCommon_GetResourceTypeStr(g_astAllBlocks[ucBlockID].ucResourceType));
            break;
        }
        default:
        {
            break;
        }
    }
}

static VOID Hv_Drv_OsdFont_RowInfoRemoveComponent(OsdRowInfo *pstRowInfo,
                        OsdComponentNode *pstComponent, BOOL bRemoveMultiple)
{
    UCHAR8 ucIndex = 0;
    BOOL bFound = HV_FALSE;
    UCHAR8 ucRowCount = 1;
    UCHAR8 ucRowIndex = 0;
    struct LinkedList *pstNextNode = NULL;
#if (HV_PROJECT_CONFIG_OSD_TEXT_WRAPPING == HV_CONFIG_ON)
    OsdWrapNode *pstWrap = NULL;

    if (Hv_Drv_OsdFont_IsWrapText(pstComponent))
    {
        pstWrap = Hv_Drv_OsdFont_FindWrapNode(pstComponent, HV_FALSE);
        if (NULL == pstWrap)
        {
            OSD_DRV_LOG_ERROR("Could not remove non-exist wrap component %s", CP_NAME(pstComponent->stCp));

            return;
        }
    }
#endif

    if (bRemoveMultiple)
    {
        ucRowCount = Hv_Drv_OsdFont_GetComponentRows(pstComponent);
    }

    for (ucRowIndex = 0; ucRowIndex < ucRowCount; ++ucRowIndex)
    {
        bFound = HV_FALSE;
        for (ucIndex = 0; ucIndex < pstRowInfo->ucComponentCount; ++ucIndex)
        {
            if (pstRowInfo->apCurrentComponents[ucIndex] == pstComponent)
            {
                bFound = HV_TRUE;
                break;
            }
        }

        if (!bFound)
        {
            OSD_DRV_LOG_ERROR("osd fatal error: remove CP-%s not found in row", CP_NAME(pstComponent->stCp));
            Hv_Drv_OsdFont_LogComponent(pstComponent);

            return;
        }

        OSD_DRV_LOG_DEBUG("remove row %u component %u", ucRowIndex, ucIndex);

        for (; ucIndex + 1 < pstRowInfo->ucComponentCount; ++ucIndex)
        {
            pstRowInfo->apCurrentComponents[ucIndex] =
                pstRowInfo->apCurrentComponents[ucIndex + 1];
        }
        if (0 == --pstRowInfo->ucComponentCount)
        {
            OSD_DRV_LOG_DEBUG("osd warning: empty row full of columns");
        }

#if (HV_OSD_CONFIG_RESERVE_SLOTS_BYROW == HV_CONFIG_OFF)
        pstRowInfo->ucResourceCount -= pstComponent->ucFontCount;
#endif

        /* next row */
#if (HV_PROJECT_CONFIG_OSD_TEXT_WRAPPING == HV_CONFIG_ON)
        if (NULL != pstWrap)
        {
            pstRowInfo = pstWrap->apDrawnRows[ucRowIndex + 1];
        }
        else
        {
#endif
            pstNextNode = pstRowInfo->stEntry.next;
            pstRowInfo = Hv_Comm_LinkedList_Entry(pstNextNode, OsdRowInfo, stEntry);
#if (HV_PROJECT_CONFIG_OSD_TEXT_WRAPPING == HV_CONFIG_ON)
        }
#endif
    }

    pstComponent->pstRowInfo = NULL;

    return;
}

static VOID AlignDrawnWidth(USHORT16 *pusWidth, UCHAR8 ucWidth)
{
    UCHAR8 ucRmd = 0;

    ucRmd = *pusWidth % ucWidth;
    if ((0 != ucRmd) && (ucRmd < HV_OSD_CONFIG_COLUMN_MINWIDTH))
    {
        *pusWidth += HV_OSD_CONFIG_COLUMN_MINWIDTH - ucRmd;
    }

    return;
}

#if (HV_PROJECT_CONFIG_OSD_MARQUEE == HV_CONFIG_ON)
static VOID Hv_Drv_OsdFont_MarqueeFixupPosition(OsdComponentNode *pstNode, USHORT16 usTotalWidth)
{
    OsdMarqueeType enMarqueeType = 0;
    OsdMarqueeNode *pstMarquee = pstNode->pstMarquee;
    OsdMarqueeSlidingParam *pstParam = NULL;
    USHORT16 usRealWidth = pstMarquee->usRealWidth;
    USHORT16 usX = 0;

    usX = Hv_Drv_OsdFont_GetComponentX(pstNode);
    pstParam = (OsdMarqueeSlidingParam*)&pstMarquee->pvMarqueeArgs;

    /* fixup initial shown position */
    enMarqueeType = Hv_Drv_OsdFont_GetMarqueeType(pstNode);
    switch (enMarqueeType)
    {
        case OSD_MARQUEE_TYPE_CYCLING_RIGHT:
        case OSD_MARQUEE_TYPE_SCROLLING_RIGHT:
        {
            if (usRealWidth > usTotalWidth)
            {
                OSD_DRV_LOG_DEBUG("marquee right, set start pos to %d", usRealWidth - usTotalWidth);

                pstMarquee->usStartPos = usRealWidth - usTotalWidth;
            }
            break;
        }
        case OSD_MARQUEE_TYPE_SLIDING_LEFT:
        {
            /* record destination X */
            if (usRealWidth > usTotalWidth)
            {
                pstParam->usDestRealX = 0;
                pstParam->usDestStartPos = usRealWidth - usTotalWidth;
            }
            else
            {
                pstParam->usDestRealX = pstMarquee->usRealX - usX;
                pstParam->usDestStartPos = 0;
            }

            pstMarquee->usRealX = usX + usTotalWidth;
            pstMarquee->usStartPos = 0;
            break;
        }
        case OSD_MARQUEE_TYPE_SLIDING_RIGHT:
        {
            /* record destination X */
            if (usRealWidth > usTotalWidth)
            {
                pstParam->usDestRealX = 0;
                pstParam->usDestStartPos = 0;
            }
            else
            {
                pstParam->usDestRealX = pstMarquee->usRealX - usX;
                pstParam->usDestStartPos = 0;
            }

            pstMarquee->usRealX = usX;
            pstMarquee->usStartPos = usRealWidth;
            break;
        }
        default:
        {
            break;
        }
    }

    return;
}

OsdMarqueeNode* Hv_Drv_OsdFont_CheckMarquee(OsdComponentNode *pstNode, USHORT16 *pusWidth, UCHAR8 ucWidth, BOOL bFixupPosition)
{
    USHORT16 usRealWidth = 0;
    USHORT16 usTotalWidth = 0;
    OsdMarqueeNode *pstMarquee = pstNode->pstMarquee;
    OsdMarqueeType enMarqueeType = 0;
    BOOL bNewCreated = HV_FALSE;

    usRealWidth = *pusWidth;
    AlignDrawnWidth(pusWidth, ucWidth);

    usTotalWidth = Hv_Drv_OsdFont_GetComponentWidth(pstNode);
    AlignDrawnWidth(&usTotalWidth, ucWidth);

#if (HV_OSD_CONFIG_MARQUEE_AUTO_CHECK_EXCESS == HV_CONFIG_ON)
    if (usRealWidth > usTotalWidth)
    {
        OSD_DRV_LOG_DEBUG("text real width: %u contained width %u, need marquee", usRealWidth, usTotalWidth);

        if (!Hv_Drv_OsdFont_GetMarqueeEnable(pstNode))
        {
            Hv_Drv_OsdFont_SetMarqueeType(pstNode, OSD_MARQUEE_TYPE_DEFAULT);
        }
    }
#endif

    if (Hv_Drv_OsdFont_GetMarqueeEnable(pstNode))
    {
        if (NULL == pstNode->pstMarquee)
        {
            pstMarquee = Hv_Drv_OsdFont_AllocMarqueeNode(pstNode);
            if (NULL == pstMarquee)
            {
                return NULL;
            }

            bNewCreated = HV_TRUE;
        }

        pstMarquee->usRealWidth = usRealWidth;

        if (bNewCreated || bFixupPosition)
        {
            /* update realwidth for sliding next marquee type */
            pstMarquee->usRealX = pstNode->stBox.usX;
            //pstMarquee->usRealWidth = usRealWidth;
            //pstNode->stBox.usX = Hv_Drv_OsdFont_GetComponentX(pstNode);

            Hv_Drv_OsdFont_MarqueeFixupPosition(pstNode, usTotalWidth);
        }

        pstNode->stBox.usX = Hv_Drv_OsdFont_GetComponentX(pstNode);

        *pusWidth = usTotalWidth;
    }

    return pstMarquee;
}
#endif

#if (HV_PROJECT_CONFIG_OSD_TEXT_WRAPPING == HV_CONFIG_ON)
static VOID Hv_Drv_OsdFont_CalculateWrapTextBox(OsdComponentNode *pstNode)
{
    const OsdBlock *pstBlock = NULL;
    USHORT16 usWidth = 0;
    UCHAR8 ucHeight = 0;
    UCHAR8 ucWidth = 0;
    OsdText *pstText = NULL;

    pstText = (OsdText*)Hv_Drv_OsdFont_GetComponentData(pstNode);
    pstBlock = &g_astAllBlocks[pstText->ucBlockID];

    Hv_Drv_OsdResource_GetSize(pstBlock->usResourceSize, &ucWidth, &ucHeight);

    pstNode->stBox.usY = pstText->usY;
    pstNode->stBox.usX = pstText->usX;

    usWidth = pstText->usWidth;
    AlignDrawnWidth(&usWidth, ucWidth);

    pstNode->stBox.usW = usWidth;
    pstNode->stBox.usH = ucHeight * pstText->ucWrapMaxRows + pstText->ucWrapRowSpace * (pstText->ucWrapMaxRows - 1);
    pstNode->ucFontCount = roundup_div(usWidth, ucWidth);

    OSD_DRV_LOG_DEBUG("Wrap Text %u Box(%u, %u, %u, %u) font count %d",
            pstText->usStringId, pstNode->stBox.usX, pstNode->stBox.usY,
            pstNode->stBox.usW, pstNode->stBox.usH, pstNode->ucFontCount);

    /* TODO: for test */
    //pstNode->stBox.usW = HV_R32(0x11029a18);
}
#endif

static VOID Hv_Drv_OsdFont_CalculateTextBox(OsdComponentNode *pstNode, BOOL bFixupPosition)
{
    OsdLanguage enCurrentLanguage = 0;
    const UCHAR8 *pucWidth = NULL;
    const USHORT16 *pusString = NULL;
    const OsdBlock *pstBlock = NULL;
    const OsdBlock *pstGlobalBlock = NULL;
    const UCHAR8 *pucGlobalWidth = NULL;
    USHORT16 usGlobalFontCount = 0;
    USHORT16 usWidth = 0;
    USHORT16 usRealWidth = 0;
    UCHAR8 ucHeight = 0;
    UCHAR8 ucWidth = 0;
    UCHAR8 ucRmd = 0;
    OsdText *pstText = NULL;
#if (HV_PROJECT_CONFIG_OSD_MARQUEE == HV_CONFIG_ON)
    USHORT16 usTotalWidth = 0;
    OsdMarqueeNode *pstMarquee = NULL;
    OsdTextIterator *pstParam = NULL;
#endif

    pstText = (OsdText*)Hv_Drv_OsdFont_GetComponentData(pstNode);

#if (HV_PROJECT_CONFIG_OSD_TEXT_WRAPPING == HV_CONFIG_ON)
    if (pstText->usIsLongText)
    {
        Hv_Drv_OsdFont_CalculateWrapTextBox(pstNode);
        return;
    }
#endif

    pstBlock = &g_astAllBlocks[pstText->ucBlockID];
    pucWidth = Hv_Drv_OsdResource_GetRealWidth(pstBlock);

    enCurrentLanguage = Hv_Drv_OsdFrame_GetCurrentLanguage();
    pusString = g_apOsdStringList[pstText->usStringId][enCurrentLanguage];

    usGlobalFontCount = pstBlock->usGlobalFontCount;
    if (usGlobalFontCount > 0)
    {
        pstGlobalBlock = &g_astAllBlocks[pstBlock->ucGlobalBlockID];
        pucGlobalWidth = Hv_Drv_OsdResource_GetRealWidth(pstGlobalBlock);
    }

    Hv_Drv_OsdResource_GetSize(pstBlock->usResourceSize, &ucWidth, (UCHAR8*)&pstNode->stBox.usH);

    while (*pusString != __END__)
    {
        if (*pusString < usGlobalFontCount)
        {
            usWidth += pucGlobalWidth[*pusString];
        }
        else
        {
            usWidth += pucWidth[*pusString - usGlobalFontCount];
        }

        pusString++;
    }

    pstNode->stBox.usRealWidth = usRealWidth = usWidth;

    pstNode->stBox.usY = pstText->usY;
    pstNode->stBox.usX = pstText->usX;
    if (pstText->usWidth > usWidth)
    {
        if (pstText->usAlignType == OSD_STRING_ALIGN_CENTER)
        {
            pstNode->stBox.usX += (pstText->usWidth - usWidth) >> 1;
        }
        else if (pstText->usAlignType == OSD_STRING_ALIGN_RIGHT)
        {
            pstNode->stBox.usX += pstText->usWidth - usWidth;
        }
    }

#if (HV_PROJECT_CONFIG_OSD_MARQUEE == HV_CONFIG_ON)
    Hv_Drv_OsdFont_CheckMarquee(pstNode, &usWidth, ucWidth, bFixupPosition);
#else
    AlignDrawnWidth(&usWidth, ucWidth);
#endif

    pstNode->stBox.usW = usWidth;
    pstNode->ucFontCount = roundup_div(usWidth, ucWidth);

    OSD_DRV_LOG_DEBUG("Text %u Box(%u, %u, %u, %u) font count %d",
            pstText->usStringId, pstNode->stBox.usX, pstNode->stBox.usY,
            pstNode->stBox.usW, pstNode->stBox.usH, pstNode->ucFontCount);
}

static VOID Hv_Drv_OsdFont_CalculateTextListBox(OsdComponentNode *pstNode, BOOL bFixupPosition)
{
    OsdLanguage enCurrentLanguage = 0;
    const UCHAR8 *pucWidth = NULL;
    const USHORT16 *pusString = NULL;
    const OsdBlock *pstBlock = NULL;
    const OsdBlock *pstGlobalBlock = NULL;
    const UCHAR8 *pucGlobalWidth = NULL;
    USHORT16 usGlobalFontCount = 0;
    USHORT16 usWidth = 0;
    USHORT16 usTotalWidth = 0;
    UCHAR8 ucWidth = 0;
    UCHAR8 ucRmd = 0;
    USHORT16 usStringID = 0;
    OsdTextList *pstTextList = NULL;

    pstTextList = (OsdTextList*)Hv_Drv_OsdFont_GetComponentData(pstNode);

    pstBlock = &g_astAllBlocks[pstTextList->ucBlockID];
    pucWidth = Hv_Drv_OsdResource_GetRealWidth(pstBlock);

    enCurrentLanguage = Hv_Drv_OsdFrame_GetCurrentLanguage();
    usStringID = pstTextList->pusStringIDList[pstTextList->ucStringCurID];
    pusString = g_apOsdStringList[usStringID][enCurrentLanguage];

    usGlobalFontCount = pstBlock->usGlobalFontCount;
    if (usGlobalFontCount > 0)
    {
        pstGlobalBlock = &g_astAllBlocks[pstBlock->ucGlobalBlockID];
        pucGlobalWidth = Hv_Drv_OsdResource_GetRealWidth(pstGlobalBlock);
    }

    Hv_Drv_OsdResource_GetSize(pstBlock->usResourceSize, &ucWidth, (UCHAR8*)&pstNode->stBox.usH);

    while (*pusString != __END__)
    {
        if (*pusString < usGlobalFontCount)
        {
            usWidth += pucGlobalWidth[*pusString];
        }
        else
        {
            usWidth += pucWidth[*pusString - usGlobalFontCount];
        }

        pusString++;
    }

    pstNode->stBox.usRealWidth = usWidth;

    pstNode->stBox.usY = pstTextList->usY;
    pstNode->stBox.usX = pstTextList->usX;
    if (pstTextList->usWidth > usWidth)
    {
        if (pstTextList->usAlignType == OSD_STRING_ALIGN_CENTER)
        {
            pstNode->stBox.usX += (pstTextList->usWidth - usWidth) >> 1;
        }
        else if (pstTextList->usAlignType == OSD_STRING_ALIGN_RIGHT)
        {
            pstNode->stBox.usX += pstTextList->usWidth - usWidth;
        }
    }

#if (HV_PROJECT_CONFIG_OSD_MARQUEE == HV_CONFIG_ON)
    Hv_Drv_OsdFont_CheckMarquee(pstNode, &usWidth, ucWidth, bFixupPosition);
#else
    AlignDrawnWidth(&usWidth, ucWidth);
#endif

    pstNode->stBox.usW = usWidth;
    pstNode->ucFontCount = roundup_div(usWidth, ucWidth);

    OSD_DRV_LOG_DEBUG("TextList %u Box(%u, %u, %u, %u) font count %d",
            usStringID, pstNode->stBox.usX, pstNode->stBox.usY,
            pstNode->stBox.usW, pstNode->stBox.usH, pstNode->ucFontCount);
}

#if (HV_PROJECT_CONFIG_OSD_TEXT_WRAPPING == HV_CONFIG_ON)
static VOID Hv_Drv_OsdFont_CalculateWrapCustomTextBox(OsdComponentNode *pstNode)
{
    const OsdBlock *pstBlock = NULL;
    USHORT16 usWidth = 0;
    UCHAR8 ucHeight = 0;
    UCHAR8 ucWidth = 0;
    OsdAsciiString *pstCustom = NULL;

    pstCustom = (OsdAsciiString*)Hv_Drv_OsdFont_GetComponentData(pstNode);
    pstBlock = &g_astAllBlocks[pstCustom->ucBlockID];

    Hv_Drv_OsdResource_GetSize(pstBlock->usResourceSize, &ucWidth, &ucHeight);

    pstNode->stBox.usY = pstCustom->usY;
    pstNode->stBox.usX = pstCustom->usX;

    usWidth = pstCustom->usWidth;
    AlignDrawnWidth(&usWidth, ucWidth);

    pstNode->stBox.usW = usWidth;
    pstNode->stBox.usH = ucHeight * pstCustom->ucWrapMaxRows + pstCustom->ucWrapRowSpace * (pstCustom->ucWrapMaxRows - 1);
    pstNode->ucFontCount = roundup_div(usWidth, ucWidth);

    OSD_DRV_LOG_DEBUG("Custom string-(%u, %u) Box(%u, %u, %u, %u) font count %d",
            pstCustom->usX, pstCustom->usY, pstNode->stBox.usX, pstNode->stBox.usY,
            pstNode->stBox.usW, pstNode->stBox.usH, pstNode->ucFontCount);
}
#endif

#if (HV_PROJECT_CONFIG_OSD_BIGFONT_SUPPORT == HV_CONFIG_ON)
static VOID Hv_Drv_OsdFont_CalculateBigIconFontBox(OsdComponentNode *pstNode)
{
    const OsdBlock *pstBlock = NULL;
    OsdAsciiString *pstAscii = NULL;
    const OsdIcon *pstIcon = NULL;
    UCHAR8 ucIndex = 0;
    UCHAR8 ucWidth = 0;
    UCHAR8 ucRows = 0;
    USHORT16 usWidth = 0;
    OsdBox *pstBox = &pstNode->stBox;

    HV_MEMSET(pstBox, 0, sizeof(*pstBox));

    pstAscii = (OsdAsciiString*)Hv_Drv_OsdFont_GetComponentData(pstNode);
    pstBlock = &g_astAllBlocks[pstAscii->ucBlockID];

    Hv_Drv_OsdResource_GetSize(pstBlock->usResourceSize, &ucWidth, (UCHAR8*)&pstNode->stBox.usH);
    for (ucIndex = 0; ucIndex < pstAscii->ucStringLength; ++ucIndex)
    {
        pstIcon = Hv_Drv_OsdFont_SearchBigFontIcon(pstAscii->ausString[ucIndex]);
        if (NULL == pstIcon)
        {
            OSD_DRV_LOG_ERROR("could not find big icon: %d", pstAscii->ausString[ucIndex]);
            return;
        }

        /* record realwidth for BigFont */
        usWidth += pstIcon->usIconCount;

        if (pstIcon->ucVCount > ucRows)
        {
            ucRows = pstIcon->ucVCount;
        }
    }

    pstBox->usRealWidth = usWidth;
    pstBox->usX = pstAscii->usX;
    pstBox->usY = pstAscii->usY;

    if (pstAscii->usWidth > usWidth)
    {
        if (pstAscii->usAlignType == OSD_STRING_ALIGN_CENTER)
        {
            pstNode->stBox.usX += (pstAscii->usWidth - usWidth) >> 1;
        }
        else if (pstAscii->usAlignType == OSD_STRING_ALIGN_RIGHT)
        {
            pstNode->stBox.usX += pstAscii->usWidth - usWidth;
        }
    }

    AlignDrawnWidth(&usWidth, ucWidth);

    pstNode->stBox.usW = usWidth;
    pstNode->stBox.usH *= ucRows;
    pstNode->ucFontCount = roundup_div(usWidth, ucWidth);
}

static VOID Hv_Drv_OsdFont_CalculateBigBmpFontBox(OsdComponentNode *pstNode)
{
    const OsdBlock *pstBlock = NULL;
    OsdAsciiString *pstAscii = NULL;
    const OsdBmp *pstBmp = NULL;
    UCHAR8 ucIndex = 0;
    UCHAR8 ucWidth = 0;
    UCHAR8 ucRows = 0;
    USHORT16 usWidth = 0;
    OsdBox *pstBox = &pstNode->stBox;

    HV_MEMSET(pstBox, 0, sizeof(*pstBox));

    pstAscii = (OsdAsciiString*)Hv_Drv_OsdFont_GetComponentData(pstNode);
    pstBlock = &g_astAllBlocks[pstAscii->ucBlockID];

    Hv_Drv_OsdResource_GetSize(pstBlock->usResourceSize, &ucWidth, (UCHAR8*)&pstNode->stBox.usH);
    for (ucIndex = 0; ucIndex < pstAscii->ucStringLength; ++ucIndex)
    {
        pstBmp = Hv_Drv_OsdFont_SearchBigFontBmp(pstAscii->ausString[ucIndex]);
        if (NULL == pstBmp)
        {
            OSD_DRV_LOG_ERROR("could not find big bmp: %d", pstAscii->ausString[ucIndex]);
            return;
        }

        if (pstBmp->ucVCount > ucRows)
        {
            ucRows = pstBmp->ucVCount;
        }

        /* record realwidth for BigFont */
        usWidth += pstBmp->usBmpCount;
    }

    pstBox->usRealWidth = usWidth;
    pstBox->usX = pstAscii->usX;
    pstBox->usY = pstAscii->usY;

    if (pstAscii->usWidth > usWidth)
    {
        if (pstAscii->usAlignType == OSD_STRING_ALIGN_CENTER)
        {
            pstNode->stBox.usX += (pstAscii->usWidth - usWidth) >> 1;
        }
        else if (pstAscii->usAlignType == OSD_STRING_ALIGN_RIGHT)
        {
            pstNode->stBox.usX += pstAscii->usWidth - usWidth;
        }
    }

    AlignDrawnWidth(&usWidth, ucWidth);

    pstNode->stBox.usW = usWidth;
    pstNode->stBox.usH *= ucRows;
    pstNode->ucFontCount = roundup_div(usWidth, ucWidth);
}

#endif
static VOID Hv_Drv_OsdFont_CalculateCustomTextBox(OsdComponentNode *pstNode, BOOL bFixupPosition)
{
    const UCHAR8 *pucWidth = NULL;
    const OsdBlock *pstBlock = NULL;
    USHORT16 usWidth = 0;
    UCHAR8 ucIndex = 0;
    UCHAR8 ucWidth = 0;
    UCHAR8 ucRmd = 0;
    USHORT16 usCharOffset = 0;
    OsdAsciiString *pstCustom = NULL;

    pstCustom = (OsdAsciiString*)Hv_Drv_OsdFont_GetComponentData(pstNode);

#if (HV_PROJECT_CONFIG_OSD_TEXT_WRAPPING == HV_CONFIG_ON)
    if (pstCustom->usIsLongText)
    {
        Hv_Drv_OsdFont_CalculateWrapCustomTextBox(pstNode);
        return;
    }
#endif

#if (HV_PROJECT_CONFIG_OSD_BIGFONT_SUPPORT == HV_CONFIG_ON)
    if (pstCustom->usIsBigIconFont)
    {
        Hv_Drv_OsdFont_CalculateBigIconFontBox(pstNode);
        return;
    }
    else if (pstCustom->usIsBigBmpFont)
    {
        Hv_Drv_OsdFont_CalculateBigBmpFontBox(pstNode);
        return;
    }
#endif

    pstBlock = &g_astAllBlocks[pstCustom->ucBlockID];
    pucWidth = Hv_Drv_OsdResource_GetRealWidth(pstBlock);

    Hv_Drv_OsdResource_GetSize(pstBlock->usResourceSize, &ucWidth, (UCHAR8*)&pstNode->stBox.usH);
    for (ucIndex = 0; ucIndex < pstCustom->ucStringLength; ++ucIndex)
    {
        usCharOffset = Hv_Drv_OsdResource_SearchFontIndexByCode(
                        pstCustom->ucBlockID, pstCustom->ausString[ucIndex]);
        usWidth += pucWidth[usCharOffset];
    }

    pstNode->stBox.usRealWidth = usWidth;

    pstNode->stBox.usY = pstCustom->usY;
    pstNode->stBox.usX = pstCustom->usX;

    if (pstCustom->usWidth > usWidth)
    {
        if (pstCustom->usAlignType == OSD_STRING_ALIGN_CENTER)
        {
            pstNode->stBox.usX += (pstCustom->usWidth - usWidth) >> 1;
        }
        else if (pstCustom->usAlignType == OSD_STRING_ALIGN_RIGHT)
        {
            pstNode->stBox.usX += pstCustom->usWidth - usWidth;
        }
    }

#if (HV_PROJECT_CONFIG_OSD_MARQUEE == HV_CONFIG_ON)
    Hv_Drv_OsdFont_CheckMarquee(pstNode, &usWidth, ucWidth, bFixupPosition);
#else
    AlignDrawnWidth(&usWidth, ucWidth);
#endif

    pstNode->stBox.usW = usWidth;
    pstNode->ucFontCount = roundup_div(usWidth, ucWidth);

    OSD_DRV_LOG_DEBUG("Custom string-(%u, %u) Box(%u, %u, %u, %u) font count %d",
            pstCustom->usX, pstCustom->usY, pstNode->stBox.usX, pstNode->stBox.usY,
            pstNode->stBox.usW, pstNode->stBox.usH, pstNode->ucFontCount);
}

static VOID Hv_Drv_OsdFont_CalculateIconBox(OsdComponentNode *pstNode)
{
    UCHAR8 ucWidth = 0;
    UCHAR8 ucHeight = 0;
    OsdIcon *pstIcon = NULL;
    const OsdBlock *pstBlock = NULL;

    pstIcon = (OsdIcon*)Hv_Drv_OsdFont_GetComponentData(pstNode);
    pstBlock = &g_astAllBlocks[pstIcon->ucBlockID];

    Hv_Drv_OsdResource_GetSize(pstBlock->usResourceSize, &ucWidth, &ucHeight);

    pstNode->stBox.usX = pstIcon->usX;
    pstNode->stBox.usY = pstIcon->usY;
    pstNode->stBox.usRealWidth = pstNode->stBox.usW = pstIcon->ucHCount * ucWidth;
    pstNode->stBox.usH = pstIcon->ucVCount * ucHeight;
    pstNode->ucFontCount = pstIcon->ucHCount;

    OSD_DRV_LOG_DEBUG("Icon %u Box(%u, %u, %u, %u) font count %d",
            pstIcon->usIconOffset, pstNode->stBox.usX, pstNode->stBox.usY,
            pstNode->stBox.usW, pstNode->stBox.usH, pstNode->ucFontCount);
}

static VOID Hv_Drv_OsdFont_CalculateIconListBox(OsdComponentNode *pstNode)
{
    UCHAR8 ucWidth = 0;
    UCHAR8 ucHeight = 0;
    OsdIconList *pstIconList = NULL;
    const OsdBlock *pstBlock = NULL;

    pstIconList = (OsdIconList*)Hv_Drv_OsdFont_GetComponentData(pstNode);
    pstBlock = &g_astAllBlocks[pstIconList->ucBlockID];

    Hv_Drv_OsdResource_GetSize(pstBlock->usResourceSize, &ucWidth, &ucHeight);

    pstNode->stBox.usX = pstIconList->usX;
    pstNode->stBox.usY = pstIconList->usY;
    pstNode->stBox.usRealWidth = pstNode->stBox.usW = pstIconList->ucHCount * ucWidth;
    pstNode->stBox.usH = pstIconList->ucVCount * ucHeight;
    pstNode->ucFontCount = pstIconList->ucHCount;

    OSD_DRV_LOG_DEBUG("IconList Box(%u, %u, %u, %u) font count %d",
            pstNode->stBox.usX, pstNode->stBox.usY,
            pstNode->stBox.usW, pstNode->stBox.usH, pstNode->ucFontCount);
}

static VOID Hv_Drv_OsdFont_CalculateBMPBox(OsdComponentNode *pstNode)
{
    UCHAR8 ucWidth = 0;
    UCHAR8 ucHeight = 0;
    OsdBmp *pstBMP = NULL;
    const OsdBlock *pstBlock = NULL;

    pstBMP = (OsdBmp*)Hv_Drv_OsdFont_GetComponentData(pstNode);
    pstBlock = &g_astAllBlocks[pstBMP->ucBlockID];

    Hv_Drv_OsdResource_GetSize(pstBlock->usResourceSize, &ucWidth, &ucHeight);

    pstNode->stBox.usX = pstBMP->usX;
    pstNode->stBox.usY = pstBMP->usY;
    pstNode->stBox.usRealWidth = pstNode->stBox.usW = pstBMP->ucHCount * ucWidth;
    pstNode->stBox.usH = pstBMP->ucVCount * ucHeight;
    pstNode->ucFontCount = pstBMP->ucHCount;

    OSD_DRV_LOG_DEBUG("BMP %u Box(%u, %u, %u, %u) font count %d",
            pstBMP->usBMPOffset, pstNode->stBox.usX, pstNode->stBox.usY,
            pstNode->stBox.usW, pstNode->stBox.usH, pstNode->ucFontCount);
}

static VOID Hv_Drv_OsdFont_CalculateBmpListBox(OsdComponentNode *pstNode)
{
    UCHAR8 ucWidth = 0;
    UCHAR8 ucHeight = 0;
    OsdBmpList *pstBmpList = NULL;
    const OsdBlock *pstBlock = NULL;

    pstBmpList = (OsdBmpList*)Hv_Drv_OsdFont_GetComponentData(pstNode);
    pstBlock = &g_astAllBlocks[pstBmpList->ucBlockID];

    Hv_Drv_OsdResource_GetSize(pstBlock->usResourceSize, &ucWidth, &ucHeight);

    pstNode->stBox.usX = pstBmpList->usX;
    pstNode->stBox.usY = pstBmpList->usY;
    pstNode->stBox.usRealWidth = pstNode->stBox.usW = pstBmpList->ucHCount * ucWidth;
    pstNode->stBox.usH = pstBmpList->ucVCount * ucHeight;
    pstNode->ucFontCount = pstBmpList->ucHCount;

    OSD_DRV_LOG_DEBUG("BmpList Box(%u, %u, %u, %u) font count %d",
            pstNode->stBox.usX, pstNode->stBox.usY,
            pstNode->stBox.usW, pstNode->stBox.usH, pstNode->ucFontCount);
}

static VOID Hv_Drv_OsdFont_CalculateNumberBox(OsdComponentNode *pstNode, BOOL bFixupPosition)
{
    const UCHAR8 *pucWidth = NULL;
    const OsdBlock *pstBlock = NULL;
    USHORT16 usWidth = 0;
    UCHAR8 ucWidth = 0;
    UCHAR8 ucRmd = 0;
    OsdNumber *pstNumber = NULL;
    INT32 iValue = 0;
    INT32 iOrigValue = 0;
    UCHAR8 aucNumbers[8] = {0, 0, 0, 0, 0, 0, 0, 0};
    UCHAR8 ucLen = 0;
    //USHORT16 usCharOffset = 0;
    USHORT16 ausCharOffsets[8] = {0, 0, 0, 0, 0, 0, 0, 0};
    BOOL bShowSignal = HV_FALSE;
#if (HV_PROJECT_CONFIG_OSD_MARQUEE == HV_CONFIG_ON)
    OsdMarqueeNode *pstMarquee = NULL;
    OsdNumberIterator *pstParam = NULL;
#endif

    pstNumber = (OsdNumber*)Hv_Drv_OsdFont_GetComponentData(pstNode);
    iOrigValue = iValue = pstNumber->iValue;

    pstBlock = &g_astAllBlocks[pstNumber->ucBlockID];
    pucWidth = Hv_Drv_OsdResource_GetRealWidth(pstBlock);

    Hv_Drv_OsdResource_GetSize(pstBlock->usResourceSize, &ucWidth, (UCHAR8*)&pstNode->stBox.usH);

    if (iValue < 0)
    {
        iValue = -iValue;
        bShowSignal = HV_TRUE;
    }
    else if (pstNumber->usShowSignSymbol)
    {
        bShowSignal = HV_TRUE;
    }

    do
    {
        aucNumbers[ucLen] = iValue % 10;
        iValue /= 10;
        ausCharOffsets[ucLen] = Hv_Drv_OsdResource_SearchFontIndexByCode(
                pstNumber->ucBlockID, aucNumbers[ucLen] + '0');
        usWidth += pucWidth[ausCharOffsets[ucLen]];
        ucLen++;
    } while (0 != iValue);

    if (bShowSignal && (0 != iOrigValue))
    {
        if (iOrigValue > 0)
        {
            ausCharOffsets[ucLen] = Hv_Drv_OsdResource_SearchFontIndexByCode(pstNumber->ucBlockID, '+');
        }
        else
        {
            ausCharOffsets[ucLen] = Hv_Drv_OsdResource_SearchFontIndexByCode(pstNumber->ucBlockID, '-');
        }
        usWidth += pucWidth[ausCharOffsets[ucLen]];
        ucLen++;
    }

    pstNode->stBox.usRealWidth = usWidth;

    pstNode->stBox.usY = pstNumber->usY;
    pstNode->stBox.usX = pstNumber->usX;
    if (pstNumber->usWidth > usWidth)
    {
        if (pstNumber->usAlignType == OSD_STRING_ALIGN_CENTER)
        {
            pstNode->stBox.usX += (pstNumber->usWidth - usWidth) >> 1;
        }
        else if (pstNumber->usAlignType == OSD_STRING_ALIGN_RIGHT)
        {
            pstNode->stBox.usX += pstNumber->usWidth - usWidth;
        }
    }

#if (HV_PROJECT_CONFIG_OSD_MARQUEE == HV_CONFIG_ON)
    pstMarquee = Hv_Drv_OsdFont_CheckMarquee(pstNode, &usWidth, ucWidth, bFixupPosition);
    if (NULL != pstMarquee)
    {
        pstParam = pstMarquee->stIterator.pvArgs;
        HV_MEMCPY(pstParam->ausCharOffsets, ausCharOffsets, sizeof(pstParam->ausCharOffsets));
        pstParam->ucLen = ucLen;
        pstParam->ucCurIndex = 0;
    }
#else
    AlignDrawnWidth(&usWidth, ucWidth);
#endif

    pstNode->stBox.usW = usWidth;
    pstNode->ucFontCount = roundup_div(usWidth, ucWidth);

    OSD_DRV_LOG_DEBUG("Number %d Box(%u, %u, %u, %u) font count %d",
            iOrigValue, pstNode->stBox.usX, pstNode->stBox.usY,
            pstNode->stBox.usW, pstNode->stBox.usH, pstNode->ucFontCount);
}

UCHAR8 Hv_Drv_OsdFont_GetCustomGroupHeight(OsdCustomGroupInfo *pstCGInfo)
{
    UCHAR8 ucIndex = 0;
    UCHAR8 ucHeight = 0;
    UCHAR8 ucMaxHeight = 0;
    OsdComponentNode *pstComponent = NULL;
    const OsdBlock *pstBlock = NULL;

    for (ucIndex = 0; ucIndex < pstCGInfo->ucComponentCount; ++ucIndex)
    {
        pstComponent = pstCGInfo->apComponents[ucIndex];

        pstBlock = Hv_Drv_OsdFont_GetBlock(pstComponent);
        Hv_Drv_OsdResource_GetSize(pstBlock->usResourceSize, NULL, &ucHeight);

        if (ucHeight > ucMaxHeight)
        {
            ucMaxHeight = ucHeight;
        }
    }

    return ucMaxHeight;
}

USHORT16 Hv_Drv_OsdFont_CalculateCustomGroupBox(OsdCustomGroupInfo *pstGroup)
{
    UCHAR8 ucIndex = 0;
    USHORT16 usX = 0;
    USHORT16 usWidth = 0;
    OsdComponentNode *pstComponent = NULL;

    for (ucIndex = 0; ucIndex < pstGroup->ucComponentCount; ++ucIndex)
    {
        pstComponent = pstGroup->apComponents[ucIndex];

        if (ucIndex > 0)
        {
            usWidth += pstGroup->aucGapWidth[ucIndex - 1];
        }

        Hv_Drv_OsdFont_CalculateComponentBox(pstComponent, HV_FALSE);
        if (ucIndex == pstGroup->ucComponentCount - 1)
        {
            usWidth += pstComponent->stBox.usW;
        }
        else
        {
            usWidth += pstComponent->stBox.usRealWidth;
        }
    }

    usX = pstGroup->usX;
    if (usWidth < pstGroup->usWidth)
    {
        if (pstGroup->usAlignType == OSD_STRING_ALIGN_CENTER)
        {
            usX += (pstGroup->usWidth - usWidth) >> 1;
        }
        else if (pstGroup->usAlignType == OSD_STRING_ALIGN_RIGHT)
        {
            usX += pstGroup->usWidth - usWidth;
        }
    }

    return usX;
}

VOID Hv_Drv_OsdFont_CalculateComponentBox(OsdComponentNode *pstNode, BOOL bFixupPosition)
{
    switch (pstNode->stCp.enType)
    {
        case OSD_CP_TYPE_TEXT:
        {
            Hv_Drv_OsdFont_CalculateTextBox(pstNode, bFixupPosition);
            break;
        }
        case OSD_CP_TYPE_TEXT_LIST:
        {
            Hv_Drv_OsdFont_CalculateTextListBox(pstNode, bFixupPosition);
            break;
        }
        case OSD_CP_TYPE_ASCII_STRING:
        {
            Hv_Drv_OsdFont_CalculateCustomTextBox(pstNode, bFixupPosition);
            break;
        }
        case OSD_CP_TYPE_ICON:
        {
            Hv_Drv_OsdFont_CalculateIconBox(pstNode);
            break;
        }
        case OSD_CP_TYPE_ICON_LIST:
        {
            Hv_Drv_OsdFont_CalculateIconListBox(pstNode);
            break;
        }
        case OSD_CP_TYPE_BMP:
        {
            Hv_Drv_OsdFont_CalculateBMPBox(pstNode);
            break;
        }
        case OSD_CP_TYPE_BMP_LIST:
        {
            Hv_Drv_OsdFont_CalculateBmpListBox(pstNode);
            break;
        }
        case OSD_CP_TYPE_NUMBER:
        {
            Hv_Drv_OsdFont_CalculateNumberBox(pstNode, bFixupPosition);
            break;
        }
        case OSD_CP_TYPE_INTACT_NUMBER:
        {
            /* TODO */
            //Hv_Drv_OsdFont_CalculateIntactNumberBox(pstNode);
            break;
        }
        default:
        {
            break;
        }
    }
#ifdef HV_SCALER_DEBUG_VERSION
    g_usTotalComponentWidth += pstNode->stBox.usW;
#endif
}

static UCHAR8 Hv_Drv_OsdFont_CalculateAvgBlankWidth(USHORT16 usWinWidth, OsdRowInfo *pstRowInfo)
{
    UCHAR8 ucIndex = 0;
    UCHAR8 ucWidth = 0;
    USHORT16 usTotalFontWidth = 0;
    UCHAR8 ucRowFontCount = 0;
    OsdComponentNode *pstNode = NULL;
    OsdCustomGroupInfo *pstInfo = NULL;

    for (ucIndex = 0; ucIndex < pstRowInfo->ucComponentCount; ++ucIndex)
    {
        pstNode = pstRowInfo->apCurrentComponents[ucIndex];

        usTotalFontWidth += pstNode->stBox.usW;
        //ucRowFontCount += pstNode->ucFontCount + 1;
        ucRowFontCount += pstNode->ucFontCount;
    }

    if (pstRowInfo->ucRowLen > ucRowFontCount)
    {
        ucWidth = ROUND_DIV(usWinWidth - usTotalFontWidth,
                pstRowInfo->ucRowLen - ucRowFontCount);
    }

    if (ucWidth < HV_OSD_CONFIG_COLUMN_MINWIDTH)
    {
        ucWidth = HV_OSD_CONFIG_COLUMN_MINWIDTH;
    }

    OSD_DRV_LOG_DEBUG("average blank width: %u", ucWidth);

    return ucWidth;
}

OsdRowInfo* Hv_Drv_OsdFont_GetRowInfoByYaxis(USHORT16 usY, BOOL bInsert)
{
    OsdRowInfo *pstRowInfo = NULL;
    OsdRowInfo *pstPrev = NULL;
    struct LinkedList *pstNode = NULL;
    UCHAR8 ucHeight = 0;
    OsdIndexInfo *pstIndexInfo = Hv_Drv_OsdIndex_GetCurrentIndexInfo();

    Hv_Comm_LinkedList_ForEach(&pstIndexInfo->stRowInfoList, pstNode)
    {
        pstRowInfo = Hv_Comm_LinkedList_Entry(pstNode, OsdRowInfo, stEntry);

        if (pstRowInfo->usY > usY)
        {
            break;
        }
        else if (pstRowInfo->usY == usY)
        {
            OSD_DRV_LOG_DEBUG("usY %d <--> 0x%x", usY, pstRowInfo);

            return pstRowInfo;
        }
        pstPrev = pstRowInfo;
    }

    if (bInsert)
    {
        pstRowInfo = Hv_Drv_OsdFont_AllocRowInfo();
        pstRowInfo->usY = usY;
        if (NULL != pstPrev)
        {
            Hv_Drv_OsdResource_GetSize(pstPrev->enSize, NULL, &ucHeight);
            if (usY < pstPrev->usY + ucHeight)
            {
                OSD_DRV_LOG_ERROR("components overlap: current Y %d prev Y %d prev height %d",
                        usY, pstPrev->usY, ucHeight);

                Hv_Drv_OsdFont_FreeRowInfo(pstRowInfo);
                return NULL;
            }

            Hv_Comm_LinkedList_AddAfter(&pstIndexInfo->stRowInfoList,
                    &pstPrev->stEntry, &pstRowInfo->stEntry);
        }
        else
        {
            Hv_Comm_LinkedList_AddHead(&pstIndexInfo->stRowInfoList,
                    &pstRowInfo->stEntry);
        }

        OSD_DRV_LOG_DEBUG("usY %d <--> 0x%x", usY, pstRowInfo);
        return pstRowInfo;
    }
    else
    {
        OSD_DRV_LOG_DEBUG("usY %d not found in rows", usY);
    }

    return NULL;
}

USHORT16 Hv_Drv_OsdFont_TakeCurrentSlot(OsdRowInfo *pstRowInfo, BOOL bIsUpdate)
{
    OsdLoadedBank *pstLoadedBank = NULL;
    OsdRowCatalog *pstCatalog = NULL;
    OsdUpdateManager *pstManager = NULL;
    OsdRadixTree *pstTree = NULL;
    USHORT16 usSlot = 0;

#if (HV_OSD_CONFIG_RESERVE_SLOTS_BYROW == HV_CONFIG_ON)
    usSlot = pstRowInfo->usBankSlot + pstRowInfo->ucCurIndex++;
#else
    pstLoadedBank = pstRowInfo->pstLoadedBank;

    if (!bIsUpdate)
    {
        usSlot = pstLoadedBank->usCurrentSlots++;
    }
    else
    {
        pstCatalog = Hv_Drv_OsdFont_GetCurrentCatalog();
        pstManager = pstCatalog->pstUpdateManager;

        if (pstManager != NULL && (pstRowInfo->pstLoadedBank == pstManager->pstUpdateBank))
        {
            OSD_DRV_LOG_DEBUG("take current allocated slot from update tree");

            pstTree = pstManager->pstUpdateSlotsManager;
        }
        else
        {
            OSD_DRV_LOG_DEBUG("take current allocated slot from redraw tree");

            pstTree = pstCatalog->pstSlotsManager;
        }

        usSlot = Hv_Drv_OsdRadix_GetLastAllocatedSlot(pstTree);
    }
#endif

    return usSlot;
}

static OsdIndexColumn* Hv_Drv_OsdFont_DrawNormalText(OsdRowInfo *pstRowInfo,
                            OsdComponentNode *pstNode, OsdIndexColumn *pstColumn,
                            BOOL bIsUpdate)
{
    OsdLanguage enCurrentLanguage = 0;
    const UCHAR8 *pucWidth = NULL;
    const USHORT16 *pusString = NULL;
    const OsdBlock *pstBlock = NULL;
    const OsdBlock *pstGlobalBlock = NULL;
    const UCHAR8 *pucGlobalWidth = NULL;
    USHORT16 usGlobalFontCount = 0;
    UCHAR8 ucWidth = 0;
    UCHAR8 ucRealWidth = 0;
    OsdText *pstText = NULL;
    const UCHAR8 *pucMatrixAddr = NULL;
    USHORT16 usOffset = 0;
    USHORT16 usSlot = 0;
    BOOL bReadOut = HV_FALSE;

    pstText = (OsdText*)Hv_Drv_OsdFont_GetComponentData(pstNode);

    pstBlock = &g_astAllBlocks[pstText->ucBlockID];
    pucWidth = Hv_Drv_OsdResource_GetRealWidth(pstBlock);

    usGlobalFontCount = pstBlock->usGlobalFontCount;
    if (usGlobalFontCount > 0)
    {
        pstGlobalBlock = &g_astAllBlocks[pstBlock->ucGlobalBlockID];
        pucGlobalWidth = Hv_Drv_OsdResource_GetRealWidth(pstGlobalBlock);
    }

    enCurrentLanguage = Hv_Drv_OsdFrame_GetCurrentLanguage();
    pusString = g_apOsdStringList[pstText->usStringId][enCurrentLanguage];

    Hv_Drv_OsdResource_GetSize(pstBlock->usResourceSize, &ucWidth, NULL);

    while (__END__ != *pusString)
    {
        if (*pusString < usGlobalFontCount)
        {
            pucMatrixAddr = Hv_Drv_OsdResource_GetBlockMatrixAddr(pstGlobalBlock);
            usOffset = *pusString;
            ucRealWidth = pucGlobalWidth[*pusString];
        }
        else
        {
            pucMatrixAddr = Hv_Drv_OsdResource_GetBlockMatrixAddr(pstBlock);
            usOffset = *pusString - usGlobalFontCount;
            ucRealWidth = pucWidth[*pusString - usGlobalFontCount];
        }

        bReadOut = Hv_Drv_OsdShift_WriteCharacter(pstRowInfo, pucMatrixAddr, usOffset, ucRealWidth);

        if (HV_TRUE == bReadOut)
        {
            usSlot = Hv_Drv_OsdFont_TakeCurrentSlot(pstRowInfo, bIsUpdate);
            Hv_Cal_OsdIndex_SetColIndex(pstColumn, usSlot);
            Hv_Cal_OsdIndex_SetColType(pstColumn, OSD_COLUMN_FONT);
            Hv_Cal_OsdIndex_SetFontColColorIndex(pstColumn, pstText->ucColorIndex);

            OSD_DRV_LOG_DEBUG("Draw Text column index %u colorIndex %d delta %d",
                    Hv_Cal_OsdIndex_GetColIndex(pstColumn),
                    pstText->ucColorIndex,
                    Hv_Cal_OsdIndex_GetColDelta(pstColumn));

            pstColumn++;
        }

        pusString++;

    }

    ucRealWidth = Hv_Drv_OsdShift_EndTransaction(HV_FALSE);
    if (0 != ucRealWidth)
    {
        usSlot = Hv_Drv_OsdFont_TakeCurrentSlot(pstRowInfo, bIsUpdate);
        Hv_Cal_OsdIndex_SetColIndex(pstColumn, usSlot);
        Hv_Cal_OsdIndex_SetColType(pstColumn, OSD_COLUMN_FONT);
        Hv_Cal_OsdIndex_SetFontColColorIndex(pstColumn, pstText->ucColorIndex);
        Hv_Cal_OsdIndex_SetColDelta(pstColumn, HV_FALSE, ucWidth - ucRealWidth);

        OSD_DRV_LOG_DEBUG("Draw Text column index %u colorIndex %d delta %d",
                Hv_Cal_OsdIndex_GetColIndex(pstColumn),
                pstText->ucColorIndex,
                Hv_Cal_OsdIndex_GetColDelta(pstColumn));

        pstColumn++;
    }

    return pstColumn;
}

static OsdIndexColumn* Hv_Drv_OsdFont_DrawText(OsdRowInfo *pstRowInfo,
                            OsdComponentNode *pstNode, OsdIndexColumn *pstColumn,
                            BOOL bIsUpdate)
{
    OsdText *pstText = NULL;

#if (HV_PROJECT_CONFIG_OSD_MARQUEE == HV_CONFIG_ON)
    OsdMarqueeNode *pstMarquee = NULL;
#endif

#if (HV_PROJECT_CONFIG_OSD_TEXT_WRAPPING == HV_CONFIG_ON)
    pstText = (OsdText*)Hv_Drv_OsdFont_GetComponentData(pstNode);
    if (pstText->usIsLongText)
    {
        return Hv_Drv_OsdFont_DrawWrapText(pstRowInfo, pstNode, pstColumn, bIsUpdate);
    }
#endif

#if (HV_PROJECT_CONFIG_OSD_MARQUEE == HV_CONFIG_ON)
    if (Hv_Drv_OsdFont_GetMarqueeEnable(pstNode))
    {
        pstMarquee = pstNode->pstMarquee;
        if (NULL != pstMarquee)
        {
            pstMarquee->pstRowInfo = pstRowInfo;
            pstMarquee->pstColumn = pstColumn;

            return Hv_Drv_OsdFont_DrawMarqueeText(pstMarquee, bIsUpdate);
        }
        else
        {
            return pstColumn;
        }
    }
    else
    {
#endif
        return Hv_Drv_OsdFont_DrawNormalText(pstRowInfo, pstNode, pstColumn, bIsUpdate);
#if (HV_PROJECT_CONFIG_OSD_MARQUEE == HV_CONFIG_ON)
    }
#endif
}

static OsdIndexColumn* Hv_Drv_OsdFont_DrawTextList(OsdRowInfo *pstRowInfo,
                    OsdComponentNode *pstNode, OsdIndexColumn *pstColumn,
                    BOOL bIsUpdate)
{
    OsdText stText;
    OsdComponentNode stNode;
    OsdTextList *pstTextList = NULL;
#if (HV_PROJECT_CONFIG_OSD_MARQUEE == HV_CONFIG_ON)
    OsdMarqueeNode *pstMarquee = NULL;
#endif

    pstTextList = (OsdTextList*)Hv_Drv_OsdFont_GetComponentData(pstNode);

#if (HV_PROJECT_CONFIG_OSD_MARQUEE == HV_CONFIG_ON)
    if (Hv_Drv_OsdFont_GetMarqueeEnable(pstNode))
    {
        pstMarquee = pstNode->pstMarquee;
        if (NULL != pstMarquee)
        {
            pstMarquee->pstRowInfo = pstRowInfo;
            pstMarquee->pstColumn = pstColumn;

            return Hv_Drv_OsdFont_DrawMarqueeText(pstMarquee, bIsUpdate);
        }
        else
        {
            return pstColumn;
        }
    }
    else
    {
#endif
        HV_MEMSET(&stNode, 0, sizeof(stNode));

        stText.ucBlockID = pstTextList->ucBlockID;
        stText.usStringId = pstTextList->pusStringIDList[pstTextList->ucStringCurID];
        stText.ucColorIndex = pstTextList->ucColorIndex;

        stNode.stCp.enType = OSD_CP_TYPE_TEXT;
        stNode.pvCpData = &stText;

        return Hv_Drv_OsdFont_DrawText(pstRowInfo, &stNode, pstColumn, bIsUpdate);
#if (HV_PROJECT_CONFIG_OSD_MARQUEE == HV_CONFIG_ON)
    }
#endif
}

#if (HV_PROJECT_CONFIG_OSD_CPU_CANVAS_SUPPORT == HV_CONFIG_ON)
static OsdIndexColumn* Hv_Drv_OsdFont_DrawCPUCanvas(OsdRowInfo *pstRowInfo,
                    OsdComponentNode *pstNode, OsdIndexColumn *pstColumn,
                    BOOL bIsUpdate)
{
    UCHAR8 ucIndex = 0;
    UCHAR8 ucColumns = 0;
    USHORT16 usSlot = 0;
    OsdColumnType enColumnType = 0;
    UCHAR8 ucColorIndex = 0;
    OsdBmp *pstBMP = NULL;

    ucColorIndex = Hv_Drv_OsdFont_GetComponentColorIndex(pstNode);
    ucColumns = Hv_Drv_OsdFont_GetComponentColumns(pstNode);

    if (bIsUpdate)
    {
        HV_MEMCPY(pstColumn, s_pstFontParam->aucCPUCanvasColumns, ucColumns * OSD_INDEX_COLUMN_SIZE);
        pstRowInfo->pstCPUCanvasStartColumn = pstColumn;

        return pstColumn + ucColumns;
    }

    pstRowInfo->pstCPUCanvasStartColumn = pstColumn;
    pstRowInfo->ucCPUCanvasColumnCount = ucColumns;
    pstRowInfo->stCPUCanvasComponent = pstNode->stCp;

    if (pstNode->stCp.enType == OSD_CP_TYPE_ICON)
    {
        enColumnType = OSD_COLUMN_FONT;
    }
    else
    {
        enColumnType = OSD_COLUMN_BMP;
    }

    /* load resource */
    usSlot = Hv_Drv_OsdResource_PickBankLoadMatrix(pstRowInfo->pstLoadedBank, NULL, 0, ucColumns);

    for (ucIndex = 0; ucIndex < ucColumns; ++ucIndex)
    {
        Hv_Cal_OsdIndex_SetColType(pstColumn, enColumnType);
        Hv_Cal_OsdIndex_SetColIndex(pstColumn, usSlot++);

        if (enColumnType == OSD_COLUMN_FONT)
        {
            Hv_Cal_OsdIndex_SetFontColColorIndex(pstColumn, ucColorIndex);
        }
        else
        {
            pstBMP = (OsdBmp*)Hv_Drv_OsdFont_GetComponentData(pstNode);
            Hv_Cal_OsdIndex_SetBMPColColorIndex(pstColumn, pstBMP->usPaletteOffset);
        }

        ++pstColumn;
    }

#ifdef CPUCANVAS_TEST
    s_pstCPUCanvasNode = pstNode;
#endif
    return pstColumn;
}

Status Hv_Drv_OsdFont_ClearCPUCanvas(OsdComponentNode *pstComponent)
{
    UCHAR8 ucRows = 0;
    UCHAR8 ucColumns = 0;
    UCHAR8 ucIndex = 0;
    USHORT16 usY = 0;
    USHORT16 usBytes = 0;
    USHORT16 usSlot = 0;
    OsdRowInfo *pstRowInfo = NULL;
    UINT32 uiOffset = 0;
    UCHAR8 ucWindowIndex = 0;
    UCHAR8 ucPhyWinId = 0;
    const OsdWindowProp *pstWindowProp = NULL;

    ucWindowIndex = Hv_Drv_OsdFont_GetComponentWindowIndex(pstComponent);
    pstWindowProp = OSD_GET_WINDOW_PROP(ucWindowIndex);
    ucPhyWinId = pstWindowProp->ucWindowID;

    Hv_Drv_OsdWindow_SetCurrentWindow(ucPhyWinId);

    ucRows = Hv_Drv_OsdFont_GetComponentRows(pstComponent);
    ucColumns = Hv_Drv_OsdFont_GetComponentColumns(pstComponent);
    usY = Hv_Drv_OsdFont_GetComponentY(pstComponent);

    pstRowInfo = Hv_Drv_OsdFont_GetRowInfoByYaxis(usY, HV_FALSE);
    HV_ASSERT_TRUE_RET(NULL != pstRowInfo, HV_FAILURE);

    usBytes = Hv_Drv_OsdCommon_GetResourceBytes(pstRowInfo->enSize, pstRowInfo->enType);
    for (ucIndex = 0; ucIndex < ucRows; ++ucIndex)
    {
        usSlot = Hv_Cal_OsdIndex_GetColIndex(pstRowInfo->pstCPUCanvasStartColumn);
        uiOffset = (UINT32)OSD_RESOURCE_DDR_ADDR(pstRowInfo->pstLoadedBank->usCurrentResourceOffset + (UINT32)usSlot * usBytes);
        HV_MEMSET((VOID*)uiOffset, 0, usBytes * pstRowInfo->ucCPUCanvasColumnCount);

        Hv_Drv_OsdResource_SetChangeRange(OSD_SRAM_CHANGE_PARTIAL, pstRowInfo->pstLoadedBank->usCurrentResourceOffset + (UINT32)usSlot * usBytes,
                pstRowInfo->pstLoadedBank->usCurrentResourceOffset + (UINT32)usSlot * usBytes + ucColumns * usBytes);

        pstRowInfo = Hv_Comm_LinkedList_Entry(pstRowInfo->stEntry.next, OsdRowInfo, stEntry);
    }

    return HV_SUCCESS;
}

static OsdRowInfo* Hv_Drv_OsdFont_SearchFirstCPUCanvasRow(OsdComponent stCp)
{
    OsdRowInfo *pstRowInfo = NULL;
    struct LinkedList *pstNode = NULL;
    OsdIndexInfo *pstIndexInfo = Hv_Drv_OsdIndex_GetCurrentIndexInfo();

    Hv_Comm_LinkedList_ForEach(&pstIndexInfo->stRowInfoList, pstNode)
    {
        pstRowInfo = Hv_Comm_LinkedList_Entry(pstNode, OsdRowInfo, stEntry);

        if ((0 == pstRowInfo->ucCPUCanvasColumnCount) || (!CP_COMPARE(pstRowInfo->stCPUCanvasComponent, stCp)))
        {
            continue;
        }

        return pstRowInfo;
    }

    return NULL;
}

Status Hv_Drv_OsdFont_DrawCPUCanvasPoint(OsdComponentNode *pstComponent, USHORT16 usX, USHORT16 usY, UINT32 uiColor, BOOL bIsClear)
{
    UCHAR8 ucIndex = 0;
    UCHAR8 ucRmd = 0;
    UCHAR8 ucWindowIndex = 0;
    UCHAR8 ucPhyWinId = 0;
    UCHAR8 ucRowIndex = 0;
    UCHAR8 ucRowRmd = 0;
    UCHAR8 ucColumnIndex = 0;
    UCHAR8 ucColumnRmd = 0;
    UCHAR8 ucRows = 0;
    UCHAR8 ucColumns = 0;
    UCHAR8 ucWidth = 0;
    UCHAR8 ucHeight = 0;
    UCHAR8 ucBits = 0;
    UCHAR8 ucValue = 0;
    UCHAR8 ucMask = 0;
    USHORT16 usBits = 0;
    USHORT16 usBytes = 0;
    USHORT16 usColors = 0;
    USHORT16 usColorOffset = 0;
    USHORT16 usSlot = 0;
    USHORT16 usResourceOffset = 0;
    UINT32 uiColorReal = 0;
    OsdPhyFontInfo *pstFontInfo = NULL;
    OsdIndexInfo *pstIndexInfo = NULL;
    OsdRowInfo *pstRowInfo = NULL;
    OsdIndexColumn *pstColumn = NULL;
    UCHAR8 *pucMatrix = NULL;
    const OsdWindowProp *pstWindowProp = NULL;
    BOOL bFound = HV_FALSE;

    ucWindowIndex = Hv_Drv_OsdFont_GetComponentWindowIndex(pstComponent);
    pstWindowProp = OSD_GET_WINDOW_PROP(ucWindowIndex);
    ucPhyWinId = pstWindowProp->ucWindowID;

    Hv_Drv_OsdWindow_SetCurrentWindow(ucPhyWinId);
    pstFontInfo = Hv_Drv_OsdFont_GetCurrentFontInfo();
    pstIndexInfo = &pstFontInfo->stIndexInfo;

    pstRowInfo = Hv_Drv_OsdFont_SearchFirstCPUCanvasRow(pstComponent->stCp);
    if (NULL == pstRowInfo)
    {
        OSD_DRV_LOG_ERROR("Osd Could not find canvas of CP-%s", CP_NAME(pstComponent->stCp));

        return HV_FAILURE;
    }

    Hv_Drv_OsdResource_GetSize(pstRowInfo->enSize, &ucWidth, &ucHeight);
    ucColumnIndex = usX / ucWidth;
    ucColumnRmd = usX % ucWidth;
    ucRowIndex = usY / ucHeight;
    ucRowRmd = usY % ucHeight;

    ucRows = Hv_Drv_OsdFont_GetComponentRows(pstComponent);
    ucColumns = Hv_Drv_OsdFont_GetComponentColumns(pstComponent);
    if ((ucRowIndex >= ucRows) || (ucColumnIndex >= ucColumns))
    {
        OSD_DRV_LOG_ERROR("Point(%d, %d) exceeds Osd CPU Canvas area(0, 0, %d, %d)", usX, usY,
                (USHORT16)ucRows * ucWidth, (USHORT16)ucColumns * ucHeight);
        return HV_FAILURE;
    }

    ucBits = Hv_Drv_OsdResource_GetBits(pstRowInfo->enType);

    /* find the drawing row */
    for (ucIndex = 0; ucIndex < ucRowIndex; ++ucIndex)
    {
        pstRowInfo = Hv_Comm_LinkedList_Entry(pstRowInfo->stEntry.next, OsdRowInfo, stEntry);
    }
    HV_ASSERT_TRUE_RET(pstRowInfo->pstCPUCanvasStartColumn != NULL, HV_FAILURE);

    /* find the drawing column */
    pstColumn = pstRowInfo->pstCPUCanvasStartColumn;
    for (ucIndex = 0; ucIndex < ucColumnIndex; ++ucIndex)
    {
        ++pstColumn;
    }

    if (!bIsClear)
    {
        if (pstComponent->stCp.enType == OSD_CP_TYPE_ICON)
        {
            ucValue = (1 << ucBits) - 1;
        }
        else
        {
            /* search color */
            usColorOffset = Hv_Cal_OsdIndex_GetBMPColColorIndex(pstColumn);
            usColors = 1 << ucBits;

            /* 工具会把颜色转成RGB565的 */
            uiColorReal = uiColor & (0xf8fcf8);
            for (ucIndex = 1; ucIndex < usColors; ++ucIndex)
            {
                if (g_auiOsdPalettesList[usColorOffset + ucIndex] == uiColorReal)
                {
                    bFound = HV_TRUE;
                    break;
                }
            }
            if (!bFound)
            {
                OSD_DRV_LOG_ERROR("Could not find canvas color: 0x%x, real color: 0x%x", uiColor, uiColorReal);
                return HV_FAILURE;
            }

            ucValue = ucIndex;
        }
    }

    usBytes = Hv_Drv_OsdCommon_GetResourceBytes(pstRowInfo->enSize, pstRowInfo->enType);
    usSlot = Hv_Cal_OsdIndex_GetColIndex(pstColumn);
    usResourceOffset = pstRowInfo->pstLoadedBank->usCurrentResourceOffset + usSlot * usBytes;
    pucMatrix = (UCHAR8*)OSD_RESOURCE_DDR_ADDR(usResourceOffset);
    /* find the bit */
    usBits = ((USHORT16)ucRowRmd * ucWidth + ucColumnRmd) * ucBits;
    ucIndex = usBits >> 3;
    ucRmd = usBits & 0x07;
    ucMask = ((1 << ucBits) - 1) << ucRmd;

    pucMatrix[ucIndex] = (pucMatrix[ucIndex] & (~ucMask)) | (ucValue << ucRmd);

    /* mark update region */
    Hv_Drv_OsdResource_SetChangeRange(OSD_SRAM_CHANGE_PARTIAL, usResourceOffset, usResourceOffset + usBytes);

    return HV_SUCCESS;
}
#endif

static OsdIndexColumn* Hv_Drv_OsdFont_DrawIcon(OsdRowInfo *pstRowInfo,
                    OsdComponentNode *pstNode, OsdIndexColumn *pstColumn,
                    BOOL bIsUpdate)
{
    USHORT16 usY = pstRowInfo->usY;
    const OsdBlock *pstBlock = NULL;
    OsdLoadedBank *pstLoadedBank = NULL;
    OsdPhyFontInfo *pstFontInfo = NULL;
    USHORT16 usWidth = 0;
    UCHAR8 ucIndex = 0;
    UCHAR8 ucWidth = 0;
    UCHAR8 ucHeight = 0;
    USHORT16 usBankOffset = 0;
    USHORT16 usIconOffset = 0;
    OsdIcon *pstIcon = NULL;
    const UCHAR8 *pucMatrixAddr = NULL;
    const USHORT16 *pusIconOffsetList = NULL;
    USHORT16 usSlot = 0;

#if (HV_PROJECT_CONFIG_OSD_CPU_CANVAS_SUPPORT == HV_CONFIG_ON)
    if (Hv_Drv_OsdFont_IsCPUCanvas(pstNode))
    {
        return Hv_Drv_OsdFont_DrawCPUCanvas(pstRowInfo, pstNode, pstColumn, bIsUpdate);
    }
#endif

    pstFontInfo = Hv_Drv_OsdFont_GetCurrentFontInfo();

    pstIcon = (OsdIcon*)Hv_Drv_OsdFont_GetComponentData(pstNode);
    pstBlock = &g_astAllBlocks[pstIcon->ucBlockID];

    Hv_Drv_OsdResource_GetSize(pstBlock->usResourceSize, &ucWidth, &ucHeight);
    usIconOffset = ((usY - pstIcon->usY) / ucHeight) * pstIcon->ucHCount;

    pucMatrixAddr = Hv_Drv_OsdResource_GetBlockMatrixAddr(pstBlock);

#if (HV_OSD_CONFIG_ICON_COMPRESSED == HV_CONFIG_ON)

    pusIconOffsetList = Hv_Drv_OsdResource_GetBlockCode(pstBlock);
    /* 第一行拷贝所有资源 */
#if (HV_OSD_CONFIG_FONT_AS_BACKGROUND == HV_CONFIG_ON)
    if (0 == usIconOffset && pstFontInfo->bIsBackground)
    {
        pstLoadedBank = Hv_Drv_OsdResource_GetCurrentLoadedBank();
        pstNode->usBankSlot = Hv_Drv_OsdResource_PickBankLoadMatrix(pstLoadedBank,
                pucMatrixAddr, pusIconOffsetList[pstIcon->usIconOffset],
                pstIcon->usIconCount);
    }
    else if (!pstFontInfo->bIsBackground)
    {
#endif

#if (HV_OSD_CONFIG_RESERVE_SLOTS_BYROW == HV_CONFIG_ON)
        usBankOffset = Hv_Drv_OsdResource_PickRowLoadMatrix(
                pstRowInfo, pucMatrixAddr,
                pusIconOffsetList[pstIcon->usIconOffset + usIconOffset], 1);

        for (ucIndex = 1; ucIndex < pstIcon->ucHCount; ++ucIndex)
        {
            Hv_Drv_OsdResource_PickRowLoadMatrix(
                    pstRowInfo, pucMatrixAddr,
                    pusIconOffsetList[pstIcon->usIconOffset + usIconOffset + ucIndex], 1);
        }
#endif

#if (HV_OSD_CONFIG_FONT_AS_BACKGROUND == HV_CONFIG_ON)
    }
#endif

#elif (HV_OSD_CONFIG_RESERVE_SLOTS_BYROW == HV_CONFIG_ON)
    usBankOffset = Hv_Drv_OsdResource_PickRowLoadMatrix(
            pstRowInfo, pucMatrixAddr, pstIcon->usIconOffset + usIconOffset, pstIcon->ucHCount);
#endif

    for (ucIndex = 0; ucIndex < pstIcon->ucHCount; ++ucIndex)
    {
#if (HV_OSD_CONFIG_FONT_AS_BACKGROUND == HV_CONFIG_ON)
        if (pstFontInfo->bIsBackground)
        {
            Hv_Cal_OsdIndex_SetColIndex(pstColumn, pstNode->usBankSlot
                    + pusIconOffsetList[pstIcon->usIconOffset + usIconOffset + ucIndex]
                    - pusIconOffsetList[pstIcon->usIconOffset]);
        }
        else
        {
#endif

#if (HV_OSD_CONFIG_RESERVE_SLOTS_BYROW == HV_CONFIG_ON)
            Hv_Cal_OsdIndex_SetColIndex(pstColumn, usBankOffset + ucIndex);
#else

#if (HV_OSD_CONFIG_BMP_COMPRESSED == HV_CONFIG_ON)
            Hv_Drv_OsdShift_CopyDirectly(pstRowInfo, pucMatrixAddr,
                    pusIconOffsetList[pstIcon->usIconOffset + usIconOffset + ucIndex]);
#else
            Hv_Drv_OsdShift_CopyDirectly(pstRowInfo, pucMatrixAddr,
                    pstIcon->usIconOffset + usIconOffset + ucIndex);
#endif
            usSlot = Hv_Drv_OsdFont_TakeCurrentSlot(pstRowInfo, bIsUpdate);
            Hv_Cal_OsdIndex_SetColIndex(pstColumn, usSlot);
#endif

#if (HV_OSD_CONFIG_FONT_AS_BACKGROUND == HV_CONFIG_ON)
        }
#endif
        Hv_Cal_OsdIndex_SetColType(pstColumn, OSD_COLUMN_FONT);
        Hv_Cal_OsdIndex_SetFontColColorIndex(pstColumn, pstIcon->ucColorIndex);
        Hv_Cal_OsdIndex_SetColDelta(pstColumn, HV_FALSE, 0);

        OSD_DRV_LOG_DEBUG("Draw Icon column index %u colorIndex %d",
                Hv_Cal_OsdIndex_GetColIndex(pstColumn),
                pstIcon->ucColorIndex);

        pstColumn++;
    }

    return pstColumn;
}

static OsdIndexColumn* Hv_Drv_OsdFont_DrawIconList(OsdRowInfo *pstRowInfo,
                    OsdComponentNode *pstNode, OsdIndexColumn *pstColumn,
                    BOOL bIsUpdate)
{
    OsdIcon stIcon;
    OsdComponentNode stNode;
    OsdIconList *pstIconList = NULL;

    HV_MEMSET(&stNode, 0, sizeof(stNode));

    pstIconList = (OsdIconList*)Hv_Drv_OsdFont_GetComponentData(pstNode);

    stIcon.ucBlockID = pstIconList->ucBlockID;
    stIcon.ucColorIndex = pstIconList->ucColorIndex;
    stIcon.ucHCount = pstIconList->ucHCount;
    stIcon.ucVCount = pstIconList->ucVCount;
    stIcon.usX = pstIconList->usX;
    stIcon.usY = pstIconList->usY;
    stIcon.usIconOffset = pstIconList->pstIconList[pstIconList->ucIconCurID].usIconOffset;
    stIcon.usIconCount = pstIconList->pstIconList[pstIconList->ucIconCurID].usIconCount;

    stNode.stCp.enType = OSD_CP_TYPE_ICON;
    stNode.pvCpData = &stIcon;

    return Hv_Drv_OsdFont_DrawIcon(pstRowInfo, &stNode, pstColumn, bIsUpdate);
}

/* WARNING: doest not support 6-bit matrix */
static VOID Hv_Drv_OsdFont_GetOneLineDrawingMap(UCHAR8 *pucBuffer, const UCHAR8 *pucMatrix, UCHAR8 ucLineIndex, UCHAR8 ucRealWidth, OsdResourceSize enSize, OsdResourceType enType)
{
    UCHAR8 ucHIndex = 0;
    UCHAR8 ucWidth = 0;
    UCHAR8 ucHeight = 0;
    UCHAR8 ucBits = 0;
    UCHAR8 ucValue = 0;
    UCHAR8 ucShift = 0;
    USHORT16 usPreBytes = 0;

    ucBits = Hv_Drv_OsdResource_GetBits(enType);
    Hv_Drv_OsdResource_GetSize(enSize, &ucWidth, &ucHeight);

    usPreBytes = (USHORT16)ucLineIndex * ucWidth * ucBits;
    pucMatrix += usPreBytes >> 3;
    ucShift = usPreBytes & 0x07;
    for (ucHIndex = 0; ucHIndex < ucRealWidth; ++ucHIndex)
    {
        ucValue = (*pucMatrix >> ucShift) & ((1 << ucBits) - 1);
        *pucBuffer++ = ucValue != 0 ? 1 : 0;
        ucShift += ucBits;
        if (ucShift >= 8)
        {
            ++pucMatrix;
            ucShift = 0;
        }
    }

    return;
}

UCHAR8* Hv_Drv_OsdFont_GetComponentDrawingmap(OsdComponentNode *pstNode, USHORT16 *pusTotalWidth, USHORT16 *pusTotalHeight)
{
    const OsdBlock *pstBlock = NULL;
    UCHAR8 ucHIndex = 0;
    UCHAR8 ucVIndex = 0;
    UCHAR8 ucLineIndex = 0;
    UCHAR8 ucRows = 0;
    UCHAR8 ucWidth = 0;
    UCHAR8 ucHeight = 0;
    UCHAR8 *pucBuffer = NULL;
    USHORT16 usOffset = 0;
    USHORT16 usBytes = 0;
    UINT32 uiTotalMapSize = 0;
    VOID *pvCpData = NULL;
    const UCHAR8 *pucMatrixAddr = NULL;
    const UCHAR8 *pucMatrix = NULL;
    OsdComponentIterator stIterator = {0};

    HV_ASSERT_TRUE_RET(NULL != pstNode, NULL);
    HV_ASSERT_TRUE_RET((NULL != pusTotalWidth) && (NULL != pusTotalHeight), NULL);

    if ((NULL != s_pstFontParam->pucComponentDrawingMap) && CP_COMPARE(s_pstFontParam->stDrawingCp, pstNode->stCp))
    {
        *pusTotalWidth = s_pstFontParam->usDrawingMapWidth;
        *pusTotalHeight = s_pstFontParam->usDrawingMapHeight;
        return s_pstFontParam->pucComponentDrawingMap;
    }
    else if (NULL != s_pstFontParam->pucComponentDrawingMap)
    {
        /* free drawing map of old component */
        Hv_Drv_OsdFont_FreeComponentDrawingmap();
    }

    Hv_Drv_OsdFont_IterInit(&stIterator, pstNode);

    pstBlock = Hv_Drv_OsdFont_GetBlock(pstNode);
    Hv_Drv_OsdResource_GetSize(pstBlock->usResourceSize, &ucWidth, &ucHeight);
    usBytes = Hv_Drv_OsdCommon_GetResourceBytes(pstBlock->usResourceSize, pstBlock->ucResourceType);

    /* calculate component box */
    Hv_Drv_OsdFont_CalculateComponentBox(pstNode, HV_FALSE);

    ucRows = Hv_Drv_OsdFont_GetComponentRows(pstNode);
    s_pstFontParam->usDrawingMapWidth = *pusTotalWidth = pstNode->stBox.usRealWidth;
    s_pstFontParam->usDrawingMapHeight = *pusTotalHeight = pstNode->stBox.usH;

    uiTotalMapSize = (UINT32)(*pusTotalWidth) * (*pusTotalHeight);

    OSD_DRV_LOG_DEBUG("malloc %d map area", uiTotalMapSize);

    s_pstFontParam->pucComponentDrawingMap = pucBuffer = HV_MALLOC(uiTotalMapSize);
    if (NULL == pucBuffer)
    {
        OSD_DRV_LOG_ERROR("Failed to malloc %d bytes, not enough memory", uiTotalMapSize);
        return NULL;
    }
    HV_MEMSET(pucBuffer, 0, uiTotalMapSize);

    for (ucVIndex = 0; ucVIndex < ucRows; ++ucVIndex)
    {
        for (ucLineIndex = 0; ucLineIndex < ucHeight; ++ucLineIndex)
        {
            Hv_Drv_OsdFont_IterReset(&stIterator);

            while (!Hv_Drv_OsdFont_IterIsEnd(&stIterator))
            {
                Hv_Drv_OsdFont_IterGetCurrentCharacter(&stIterator, &pucMatrixAddr, &usOffset, &ucWidth);
                pucMatrix = pucMatrixAddr + usOffset * usBytes;

                if (pucBuffer - s_pstFontParam->pucComponentDrawingMap > uiTotalMapSize - ucWidth)
                {
                    OSD_DRV_LOG_ERROR("not enough map area left: 0x%x 0x%x %d", pucBuffer, s_pstFontParam->pucComponentDrawingMap, ucWidth);
                    break;
                }
                Hv_Drv_OsdFont_GetOneLineDrawingMap(pucBuffer, pucMatrix, ucLineIndex, ucWidth, pstBlock->usResourceSize, pstBlock->ucResourceType);
                pucBuffer += ucWidth;

                Hv_Drv_OsdFont_IterStepForward(&stIterator);
            }
        }

        /* step to next row for BMP/Icon */
        Hv_Drv_OsdFont_IterStepForwardRow(&stIterator);
    }

    OSD_DRV_LOG_DEBUG("fill %d map area", pucBuffer - s_pstFontParam->pucComponentDrawingMap);

    s_pstFontParam->stDrawingCp.enType = pstNode->stCp.enType;
    s_pstFontParam->stDrawingCp.usIndex = pstNode->stCp.usIndex;

    Hv_Drv_OsdFont_IterFree(&stIterator);

    return s_pstFontParam->pucComponentDrawingMap;
}

VOID Hv_Drv_OsdFont_FreeComponentDrawingmap(VOID)
{
    if (NULL != s_pstFontParam->pucComponentDrawingMap)
    {
        HV_FREE(s_pstFontParam->pucComponentDrawingMap);

        s_pstFontParam->pucComponentDrawingMap = NULL;
    }

    s_pstFontParam->stDrawingCp.enType = OSD_CP_TYPE_NONE;
    s_pstFontParam->stDrawingCp.usIndex = 0;
    return;
}

Status Hv_Drv_OsdFont_GetFontResouceInfo(OsdComponentNode *pstNode, OsdFontResourceInfo *pstResInfo)
{
    HV_ASSERT_VALID_PTR(pstNode);
    HV_ASSERT_VALID_PTR(pstResInfo);
    const OsdBlock *pstBlock = NULL;
    const USHORT16 *pusOffsetList = NULL;

    if (pstNode->stCp.enType == OSD_CP_TYPE_ICON)
    {
        OsdIcon *pstIcon = (OsdIcon *)pstNode->pvCpData;
        HV_ASSERT_VALID_PTR(pstIcon);
        pstBlock = &g_astAllBlocks[pstIcon->ucBlockID];
        pusOffsetList = Hv_Drv_OsdResource_GetBlockCode(pstBlock);
        pstResInfo->pusMatrixOffset = &pusOffsetList[pstIcon->usIconOffset];
    }
    else if (pstNode->stCp.enType == OSD_CP_TYPE_ICON_LIST)
    {
        OsdIconList *pstIconList = (OsdIconList *)pstNode->pvCpData;
        HV_ASSERT_VALID_PTR(pstIconList);
        pstBlock = &g_astAllBlocks[pstIconList->ucBlockID];
        pusOffsetList = Hv_Drv_OsdResource_GetBlockCode(pstBlock);
        pstResInfo->pusMatrixOffset = &pusOffsetList[pstIconList->pstIconList[pstIconList->ucIconCurID].usIconOffset];
    }
    else
    {
        return HV_FAILURE;
    }

    pstResInfo->ucRows = Hv_Drv_OsdFont_GetComponentRows(pstNode);
    pstResInfo->ucColumns = Hv_Drv_OsdFont_GetComponentColumns(pstNode);
    Hv_Drv_OsdResource_GetSize(pstBlock->usResourceSize, &pstResInfo->ucResWidth, &pstResInfo->ucResHeight);
    pstResInfo->ucResBits = Hv_Drv_OsdResource_GetBits(pstBlock->ucResourceType);
    pstResInfo->usMatrixSize = Hv_Drv_OsdCommon_GetResourceBytes(pstBlock->usResourceSize, pstBlock->ucResourceType);
    pstResInfo->pucMatrixAddr = Hv_Drv_OsdResource_GetBlockMatrixAddr(pstBlock);

    OSD_DRV_LOG_DEBUG("Get %s resource info: MatrixAddr=0x%x, MatrixOffset=0x%x, MatrixSize=%d, Row=%d, Column=%d, ResWidth=%d, ResHeight=%d, ResBits=%d",
                      CP_NAME(pstNode->stCp), pstResInfo->pucMatrixAddr, pstResInfo->pusMatrixOffset, pstResInfo->usMatrixSize,
                      pstResInfo->ucRows, pstResInfo->ucColumns, pstResInfo->ucResWidth, pstResInfo->ucResHeight, pstResInfo->ucResBits);

    return HV_SUCCESS;
}

static OsdIndexColumn* Hv_Drv_OsdFont_DrawBMP(OsdRowInfo *pstRowInfo,
                OsdComponentNode *pstNode, OsdIndexColumn *pstColumn,
                BOOL bIsUpdate)
{
    OsdPhyFontInfo *pstFontInfo = NULL;
    OsdLoadedBank *pstLoadedBank = NULL;
    const OsdBlock *pstBlock = NULL;
    OsdLoadedBlock *pstLoadedBlock = NULL;
    USHORT16 usWidth = 0;
    UCHAR8 ucIndex = 0;
    UCHAR8 ucWidth = 0;
    UCHAR8 ucHeight = 0;
    USHORT16 usBankOffset = 0;
    USHORT16 usBmpOffset = 0;
    USHORT16 usSlot = 0;
    OsdBmp *pstBMP = NULL;
    USHORT16 usY = pstRowInfo->usY;
    const UCHAR8 *pucMatrixAddr = NULL;
    const USHORT16 *pusBmpOffsetList = NULL;

#if (HV_PROJECT_CONFIG_OSD_CPU_CANVAS_SUPPORT == HV_CONFIG_ON)
    if (Hv_Drv_OsdFont_IsCPUCanvas(pstNode))
    {
        return Hv_Drv_OsdFont_DrawCPUCanvas(pstRowInfo, pstNode, pstColumn, bIsUpdate);
    }
#endif

    pstFontInfo = Hv_Drv_OsdFont_GetCurrentFontInfo();

    pstBMP = (OsdBmp*)Hv_Drv_OsdFont_GetComponentData(pstNode);
    pstBlock = &g_astAllBlocks[pstBMP->ucBlockID];

    Hv_Drv_OsdResource_GetSize(pstBlock->usResourceSize, &ucWidth, &ucHeight);

    usBmpOffset = ((usY - pstBMP->usY) / ucHeight) * pstBMP->ucHCount;

    pucMatrixAddr = Hv_Drv_OsdResource_GetBlockMatrixAddr(pstBlock);

#if (HV_OSD_CONFIG_BMP_COMPRESSED == HV_CONFIG_ON)
    pusBmpOffsetList = Hv_Drv_OsdResource_GetBlockCode(pstBlock);

#if (HV_OSD_CONFIG_FONT_AS_BACKGROUND == HV_CONFIG_ON)
    if (0 == usBmpOffset && pstFontInfo->bIsBackground)
    {
        pstLoadedBank = Hv_Drv_OsdResource_GetCurrentLoadedBank();
        pstNode->usBankSlot = Hv_Drv_OsdResource_PickBankLoadMatrix(pstLoadedBank,
                pucMatrixAddr, pusBmpOffsetList[pstBMP->usBMPOffset],
                pstBMP->usBmpCount);

        OSD_DRV_LOG_DEBUG("draw bmp background, count %d", pstBMP->usBmpCount);
    }
    else if (!pstFontInfo->bIsBackground)
    {
#endif

#if (HV_OSD_CONFIG_RESERVE_SLOTS_BYROW == HV_CONFIG_ON)
        usBankOffset = Hv_Drv_OsdResource_PickRowLoadMatrix(
                pstRowInfo, pucMatrixAddr,
                pusBmpOffsetList[pstBMP->usBMPOffset + usBmpOffset], 1);

        for (ucIndex = 1; ucIndex < pstBMP->ucHCount; ++ucIndex)
        {
            Hv_Drv_OsdResource_PickRowLoadMatrix(
                    pstRowInfo, pucMatrixAddr,
                    pusBmpOffsetList[pstBMP->usBMPOffset + usBmpOffset + ucIndex], 1);
        }
#endif

#if (HV_OSD_CONFIG_FONT_AS_BACKGROUND == HV_CONFIG_ON)
    }
#endif

#elif (HV_OSD_CONFIG_RESERVE_SLOTS_BYROW == HV_CONFIG_ON)
    usBankOffset = Hv_Drv_OsdResource_PickRowLoadMatrix(
            pstRowInfo, pucMatrixAddr, pstBMP->usBMPOffset + usBmpOffset, pstBMP->ucHCount);
#endif

    for (ucIndex = 0; ucIndex < pstBMP->ucHCount; ++ucIndex)
    {
#if (HV_OSD_CONFIG_FONT_AS_BACKGROUND == HV_CONFIG_ON)
        if (pstFontInfo->bIsBackground)
        {
            Hv_Cal_OsdIndex_SetColIndex(pstColumn, pstNode->usBankSlot
                    + pusBmpOffsetList[pstBMP->usBMPOffset + usBmpOffset + ucIndex]
                    - pusBmpOffsetList[pstBMP->usBMPOffset]);
        }
        else
        {
#endif

#if (HV_OSD_CONFIG_RESERVE_SLOTS_BYROW == HV_CONFIG_ON)
            Hv_Cal_OsdIndex_SetColIndex(pstColumn, usBankOffset + ucIndex);
#else

#if (HV_OSD_CONFIG_BMP_COMPRESSED == HV_CONFIG_ON)
            Hv_Drv_OsdShift_CopyDirectly(pstRowInfo, pucMatrixAddr,
                    pusBmpOffsetList[pstBMP->usBMPOffset + usBmpOffset + ucIndex]);
#else
            Hv_Drv_OsdShift_CopyDirectly(pstRowInfo, pucMatrixAddr,
                    pstBMP->usBMPOffset + usBmpOffset + ucIndex);
#endif
            usSlot = Hv_Drv_OsdFont_TakeCurrentSlot(pstRowInfo, bIsUpdate);
            Hv_Cal_OsdIndex_SetColIndex(pstColumn, usSlot);
#endif

#if (HV_OSD_CONFIG_FONT_AS_BACKGROUND == HV_CONFIG_ON)
        }
#endif
        Hv_Cal_OsdIndex_SetColType(pstColumn, OSD_COLUMN_BMP);
        Hv_Cal_OsdIndex_SetBMPColColorIndex(pstColumn, pstBMP->usPaletteOffset);

        //OSD_DRV_LOG_DEBUG("Draw BMP column index %u colorIndex",
        //        Hv_Cal_OsdIndex_GetColIndex(pstColumn));

        pstColumn++;
    }

    return pstColumn;
}

static OsdIndexColumn* Hv_Drv_OsdFont_DrawBmpList(OsdRowInfo *pstRowInfo,
                    OsdComponentNode *pstNode, OsdIndexColumn *pstColumn,
                    BOOL bIsUpdate)
{
    OsdBmp stBmp;
    OsdComponentNode stNode;
    OsdBmpList *pstBmpList = NULL;

    HV_MEMSET(&stNode, 0, sizeof(stNode));

    pstBmpList = (OsdBmpList*)Hv_Drv_OsdFont_GetComponentData(pstNode);

    stBmp.ucBlockID = pstBmpList->ucBlockID;
    stBmp.ucHCount = pstBmpList->ucHCount;
    stBmp.ucVCount = pstBmpList->ucVCount;
    stBmp.usX = pstBmpList->usX;
    stBmp.usY = pstBmpList->usY;
    stBmp.usBMPOffset = pstBmpList->pstBmpList[pstBmpList->ucBmpCurID].usBMPOffset;
    stBmp.usBmpCount = pstBmpList->pstBmpList[pstBmpList->ucBmpCurID].usBmpCount;
    stBmp.usPaletteOffset = pstBmpList->pstBmpList[pstBmpList->ucBmpCurID].usPaletteOffset;

    stNode.stCp.enType = OSD_CP_TYPE_ICON;
    stNode.pvCpData = &stBmp;

    return Hv_Drv_OsdFont_DrawBMP(pstRowInfo, &stNode, pstColumn, bIsUpdate);
}

static OsdIndexColumn* Hv_Drv_OsdFont_DrawNormalNumber(OsdRowInfo *pstRowInfo,
                    OsdComponentNode *pstNode, OsdIndexColumn *pstColumn,
                    BOOL bIsUpdate)
{
    const UCHAR8 *pucWidth = NULL;
    const OsdBlock *pstBlock = NULL;
    OsdLoadedBlock *pstLoadedBlock = NULL;
    UCHAR8 ucWidth = 0;
    OsdNumber *pstNumber = NULL;
    INT32 iValue = 0;
    INT32 iOrigValue = 0;
    USHORT16 ausOffsets[8] = {0, 0, 0, 0, 0, 0, 0, 0};
    UCHAR8 ucLen = 0;
    UCHAR8 ucIndex = 0;
    UCHAR8 ucRmd = 0;
    const UCHAR8 *pucMatrixAddr = NULL;
    USHORT16 usBankOffset = 0;
    USHORT16 usSlot = 0;
    UCHAR8 ucRealWidth = 0;
    BOOL bShowSignal = HV_FALSE;
    BOOL bReadOut = HV_FALSE;

    pstNumber = (OsdNumber*)Hv_Drv_OsdFont_GetComponentData(pstNode);
    iOrigValue = iValue = pstNumber->iValue;

    pstBlock = &g_astAllBlocks[pstNumber->ucBlockID];
    pucWidth = Hv_Drv_OsdResource_GetRealWidth(pstBlock);

    if (iValue < 0)
    {
        iValue = -iValue;
        bShowSignal = HV_TRUE;
    }
    else if (pstNumber->usShowSignSymbol)
    {
        bShowSignal = HV_TRUE;
    }

    do
    {
        ucRmd = iValue % 10;
        ausOffsets[ucLen] = Hv_Drv_OsdResource_SearchFontIndexByCode(
                pstNumber->ucBlockID, ucRmd + '0');
        iValue /= 10;
        ucLen++;
    } while (0 != iValue);

    if (bShowSignal && (0 != iOrigValue))
    {
        if (iOrigValue > 0)
        {
            ausOffsets[ucLen++] = Hv_Drv_OsdResource_SearchFontIndexByCode(
                    pstNumber->ucBlockID, '+');
        }
        else
        {
            ausOffsets[ucLen++] = Hv_Drv_OsdResource_SearchFontIndexByCode(
                    pstNumber->ucBlockID, '-');
        }
    }

    Hv_Drv_OsdResource_GetSize(pstBlock->usResourceSize, &ucWidth, NULL);

    pucMatrixAddr = Hv_Drv_OsdResource_GetBlockMatrixAddr(pstBlock);
    for (ucIndex = ucLen; ucIndex > 0; --ucIndex)
    {
        //usBankOffset = Hv_Drv_OsdResource_PickRowLoadMatrix(
        //        pstRowInfo, pucMatrixAddr, ausOffsets[ucIndex - 1], 1);

        bReadOut = Hv_Drv_OsdShift_WriteCharacter(pstRowInfo, pucMatrixAddr, ausOffsets[ucIndex - 1],
                pucWidth[ausOffsets[ucIndex - 1]]);

        if (HV_TRUE == bReadOut)
        {
            usSlot = Hv_Drv_OsdFont_TakeCurrentSlot(pstRowInfo, bIsUpdate);
            Hv_Cal_OsdIndex_SetColIndex(pstColumn, usSlot);
            Hv_Cal_OsdIndex_SetColType(pstColumn, OSD_COLUMN_FONT);
            Hv_Cal_OsdIndex_SetFontColColorIndex(pstColumn, pstNumber->ucColorIndex);

            OSD_DRV_LOG_DEBUG("Draw Number index %u colorIndex %d delta %d",
                    Hv_Cal_OsdIndex_GetColIndex(pstColumn),
                    pstNumber->ucColorIndex,
                    Hv_Cal_OsdIndex_GetColDelta(pstColumn));

            pstColumn++;
        }
    }

    ucRealWidth = Hv_Drv_OsdShift_EndTransaction(HV_FALSE);
    if (0 != ucRealWidth)
    {
        usSlot = Hv_Drv_OsdFont_TakeCurrentSlot(pstRowInfo, bIsUpdate);
        Hv_Cal_OsdIndex_SetColIndex(pstColumn, usSlot);
        Hv_Cal_OsdIndex_SetColType(pstColumn, OSD_COLUMN_FONT);
        Hv_Cal_OsdIndex_SetFontColColorIndex(pstColumn, pstNumber->ucColorIndex);
        Hv_Cal_OsdIndex_SetColDelta(pstColumn, HV_FALSE, ucWidth - ucRealWidth);

        OSD_DRV_LOG_DEBUG("Draw Number index %u colorIndex %d delta %d",
                Hv_Cal_OsdIndex_GetColIndex(pstColumn),
                pstNumber->ucColorIndex,
                Hv_Cal_OsdIndex_GetColDelta(pstColumn));

        pstColumn++;
    }

    return pstColumn;
}

static OsdIndexColumn* Hv_Drv_OsdFont_DrawNumber(OsdRowInfo *pstRowInfo,
                            OsdComponentNode *pstNode, OsdIndexColumn *pstColumn,
                            BOOL bIsUpdate)
{
    OsdNumber *pstNumber = NULL;
#if (HV_PROJECT_CONFIG_OSD_MARQUEE == HV_CONFIG_ON)
    OsdMarqueeNode *pstMarquee = NULL;
#endif

    pstNumber = (OsdNumber*)Hv_Drv_OsdFont_GetComponentData(pstNode);
#if (HV_PROJECT_CONFIG_OSD_MARQUEE == HV_CONFIG_ON)
    if (Hv_Drv_OsdFont_GetMarqueeEnable(pstNode))
    {
        pstMarquee = pstNode->pstMarquee;
        if (NULL != pstMarquee)
        {
            pstMarquee->pstRowInfo = pstRowInfo;
            pstMarquee->pstColumn = pstColumn;

            return Hv_Drv_OsdFont_DrawMarqueeText(pstMarquee, bIsUpdate);
        }
        else
        {
            return pstColumn;
        }
    }
    else
    {
#endif
        return Hv_Drv_OsdFont_DrawNormalNumber(pstRowInfo, pstNode, pstColumn, bIsUpdate);
#if (HV_PROJECT_CONFIG_OSD_MARQUEE == HV_CONFIG_ON)
    }
#endif
}

#if (HV_PROJECT_CONFIG_OSD_BIGFONT_SUPPORT == HV_CONFIG_ON)
static OsdIndexColumn* Hv_Drv_OsdFont_DrawBigIconFontCustomText(OsdRowInfo *pstRowInfo,
                        OsdComponentNode *pstNode, OsdIndexColumn *pstColumn,
                        BOOL bIsUpdate)
{
    const OsdBlock *pstBlock = NULL;
    OsdLoadedBlock *pstLoadedBlock = NULL;
    UCHAR8 ucWidth = 0;
    UCHAR8 ucIndex = 0;
    UCHAR8 ucColIndex = 0;
    UCHAR8 ucCurWidth = 0;
    USHORT16 usCharOffset = 0;
    OsdAsciiString *pstAscii = NULL;
    const UCHAR8 *pucMatrixAddr = NULL;
    USHORT16 usBankOffset = 0;
    USHORT16 usSlot = 0;
    UCHAR8 ucRealWidth = 0;
    BOOL bReadOut = HV_FALSE;
    USHORT16 usY = pstRowInfo->usY;
    OsdLoadedBank *pstLoadedBank = NULL;
    UCHAR8 ucHeight = 0;
    USHORT16 usIconOffset = 0;
    const OsdIcon *pstIcon = NULL;
    const USHORT16 *pusIconOffsetList = NULL;

    pstAscii = (OsdAsciiString*)Hv_Drv_OsdFont_GetComponentData(pstNode);

    pstBlock = &g_astAllBlocks[pstAscii->ucBlockID];
    pusIconOffsetList = Hv_Drv_OsdResource_GetBlockCode(pstBlock);

    Hv_Drv_OsdResource_GetSize(pstBlock->usResourceSize, &ucWidth, &ucHeight);

    if (0 == pstAscii->ucStringLength)
    {
        OSD_DRV_LOG_WARN("CP-%s BigFont string length is 0", CP_NAME(pstNode->stCp));
    }
    else if (pstAscii->ucStringLength > OSD_ASCII_STRING_LENGTH_MAX)
    {
        OSD_DRV_LOG_WARN("CP-%s BigFont string length %d exceeds max %d", CP_NAME(pstNode->stCp),
                pstAscii->ucStringLength, OSD_ASCII_STRING_LENGTH_MAX);

        pstAscii->ucStringLength = OSD_ASCII_STRING_LENGTH_MAX;
    }

    pucMatrixAddr = Hv_Drv_OsdResource_GetBlockMatrixAddr(pstBlock);

    for (ucIndex = 0; ucIndex < pstAscii->ucStringLength; ++ucIndex)
    {
        pstIcon = Hv_Drv_OsdFont_SearchBigFontIcon(pstAscii->ausString[ucIndex]);
        if (NULL == pstIcon)
        {
            OSD_DRV_LOG_ERROR("Osd fatal: failed to find icon code: %d of CP-%s", pstAscii->ausString[ucIndex], CP_NAME(pstNode->stCp));

            return pstColumn;
        }

        usIconOffset = ((usY - pstAscii->usY) / ucHeight) * pstIcon->ucHCount;

        for (ucColIndex = 0; ucColIndex < pstIcon->ucHCount; ++ucColIndex)
        {
            if (ucColIndex != (pstIcon->ucHCount - 1))
            {
                ucCurWidth = ucWidth;
            }
            else
            {
                /* usX stores real width */
                ucCurWidth = pstIcon->usX % ucWidth;
            }

            bReadOut = Hv_Drv_OsdShift_WriteCharacter(pstRowInfo, pucMatrixAddr,
                    pusIconOffsetList[pstIcon->usIconOffset + usIconOffset + ucColIndex], ucCurWidth);

            if (HV_TRUE == bReadOut)
            {
                usSlot = Hv_Drv_OsdFont_TakeCurrentSlot(pstRowInfo, bIsUpdate);

                Hv_Cal_OsdIndex_SetColIndex(pstColumn, usSlot);
                Hv_Cal_OsdIndex_SetColType(pstColumn, OSD_COLUMN_FONT);
                Hv_Cal_OsdIndex_SetFontColColorIndex(pstColumn, pstAscii->ucColorIndex);
                Hv_Cal_OsdIndex_SetColDelta(pstColumn, HV_FALSE, 0);

                OSD_DRV_LOG_DEBUG("Draw BigFont Icon column index %u colorIndex %d",
                        Hv_Cal_OsdIndex_GetColIndex(pstColumn),
                        pstAscii->ucColorIndex);

                pstColumn++;
            }
        }
    }

    ucRealWidth = Hv_Drv_OsdShift_EndTransaction(HV_FALSE);
    if (0 != ucRealWidth)
    {
        usSlot = Hv_Drv_OsdFont_TakeCurrentSlot(pstRowInfo, bIsUpdate);
        Hv_Cal_OsdIndex_SetColIndex(pstColumn, usSlot);
        Hv_Cal_OsdIndex_SetColType(pstColumn, OSD_COLUMN_FONT);
        Hv_Cal_OsdIndex_SetFontColColorIndex(pstColumn, pstAscii->ucColorIndex);
        Hv_Cal_OsdIndex_SetColDelta(pstColumn, HV_FALSE, ucWidth - ucRealWidth);

        OSD_DRV_LOG_DEBUG("Draw BigFont Icon index %u colorIndex %d delta %d",
                Hv_Cal_OsdIndex_GetColIndex(pstColumn), pstAscii->ucColorIndex,
                Hv_Cal_OsdIndex_GetColDelta(pstColumn));

        pstColumn++;
    }

    return pstColumn;
}

static OsdIndexColumn* Hv_Drv_OsdFont_DrawBigBmpFontCustomText(OsdRowInfo *pstRowInfo,
                        OsdComponentNode *pstNode, OsdIndexColumn *pstColumn,
                        BOOL bIsUpdate)
{
    const OsdBlock *pstBlock = NULL;
    OsdLoadedBlock *pstLoadedBlock = NULL;
    UCHAR8 ucWidth = 0;
    UCHAR8 ucIndex = 0;
    UCHAR8 ucColIndex = 0;
    UCHAR8 ucCurWidth = 0;
    USHORT16 usCharOffset = 0;
    OsdAsciiString *pstAscii = NULL;
    const UCHAR8 *pucMatrixAddr = NULL;
    USHORT16 usBankOffset = 0;
    USHORT16 usSlot = 0;
    UCHAR8 ucRealWidth = 0;
    BOOL bReadOut = HV_FALSE;
    USHORT16 usY = pstRowInfo->usY;
    OsdLoadedBank *pstLoadedBank = NULL;
    UCHAR8 ucHeight = 0;
    USHORT16 usBmpOffset = 0;
    const OsdBmp *pstBmp = NULL;
    const USHORT16 *pusBmpOffsetList = NULL;

    pstAscii = (OsdAsciiString*)Hv_Drv_OsdFont_GetComponentData(pstNode);

    pstBlock = &g_astAllBlocks[pstAscii->ucBlockID];
    pusBmpOffsetList = Hv_Drv_OsdResource_GetBlockCode(pstBlock);

    Hv_Drv_OsdResource_GetSize(pstBlock->usResourceSize, &ucWidth, &ucHeight);

    if (0 == pstAscii->ucStringLength)
    {
        OSD_DRV_LOG_WARN("CP-%s BigBmpFont string length is 0", CP_NAME(pstNode->stCp));
    }
    else if (pstAscii->ucStringLength > OSD_ASCII_STRING_LENGTH_MAX)
    {
        OSD_DRV_LOG_WARN("CP-%s BigBmpFont string length %d exceeds max %d", CP_NAME(pstNode->stCp),
                pstAscii->ucStringLength, OSD_ASCII_STRING_LENGTH_MAX);

        pstAscii->ucStringLength = OSD_ASCII_STRING_LENGTH_MAX;
    }

    pucMatrixAddr = Hv_Drv_OsdResource_GetBlockMatrixAddr(pstBlock);

    for (ucIndex = 0; ucIndex < pstAscii->ucStringLength; ++ucIndex)
    {
        pstBmp = Hv_Drv_OsdFont_SearchBigFontBmp(pstAscii->ausString[ucIndex]);
        if (NULL == pstBmp)
        {
            OSD_DRV_LOG_ERROR("Osd fatal: failed to find bmp code: %d of CP-%s", pstAscii->ausString[ucIndex], CP_NAME(pstNode->stCp));

            return pstColumn;
        }

        usBmpOffset = ((usY - pstBmp->usY) / ucHeight) * pstBmp->ucHCount;

        for (ucColIndex = 0; ucColIndex < pstBmp->ucHCount; ++ucColIndex)
        {
            if (ucColIndex != (pstBmp->ucHCount - 1))
            {
                ucCurWidth = ucWidth;
            }
            else
            {
                /* usX stores real width */
                ucCurWidth = pstBmp->usX % ucWidth;
            }

            bReadOut = Hv_Drv_OsdShift_WriteCharacter(pstRowInfo, pucMatrixAddr,
                    pusBmpOffsetList[pstBmp->usBMPOffset + usBmpOffset + ucColIndex], ucCurWidth);

            if (HV_TRUE == bReadOut)
            {
                usSlot = Hv_Drv_OsdFont_TakeCurrentSlot(pstRowInfo, bIsUpdate);

                Hv_Cal_OsdIndex_SetColIndex(pstColumn, usSlot);
                Hv_Cal_OsdIndex_SetColType(pstColumn, OSD_COLUMN_BMP);
                Hv_Cal_OsdIndex_SetBMPColColorIndex(pstColumn, pstAscii->ucColorIndex);

                OSD_DRV_LOG_DEBUG("Draw BigFont Bmp column index %u colorIndex %d",
                        Hv_Cal_OsdIndex_GetColIndex(pstColumn),
                        pstAscii->ucColorIndex);

                pstColumn++;
            }
        }
    }

    ucRealWidth = Hv_Drv_OsdShift_EndTransaction(HV_FALSE);
    if (0 != ucRealWidth)
    {
        usSlot = Hv_Drv_OsdFont_TakeCurrentSlot(pstRowInfo, bIsUpdate);
        Hv_Cal_OsdIndex_SetColIndex(pstColumn, usSlot);
        Hv_Cal_OsdIndex_SetColType(pstColumn, OSD_COLUMN_BMP);
        Hv_Cal_OsdIndex_SetBMPColColorIndex(pstColumn, pstAscii->ucColorIndex);

        OSD_DRV_LOG_DEBUG("Draw BigFont Bmp index %u colorIndex %d",
                Hv_Cal_OsdIndex_GetColIndex(pstColumn), pstAscii->ucColorIndex);

        pstColumn++;
    }

    return pstColumn;
}
#endif

static OsdIndexColumn* Hv_Drv_OsdFont_DrawNormalCustomText(OsdRowInfo *pstRowInfo,
                        OsdComponentNode *pstNode, OsdIndexColumn *pstColumn,
                        BOOL bIsUpdate)
{
    const UCHAR8 *pucWidth = NULL;
    USHORT16 *pusString = NULL;
    const OsdBlock *pstBlock = NULL;
    OsdLoadedBlock *pstLoadedBlock = NULL;
    UCHAR8 ucWidth = 0;
    UCHAR8 ucIndex = 0;
    USHORT16 usCharOffset = 0;
    OsdAsciiString *pstCustom = NULL;
    const UCHAR8 *pucMatrixAddr = NULL;
    USHORT16 usBankOffset = 0;
    USHORT16 usSlot = 0;
    UCHAR8 ucRealWidth = 0;
    BOOL bReadOut = HV_FALSE;

    pstCustom = (OsdAsciiString*)Hv_Drv_OsdFont_GetComponentData(pstNode);

#if (HV_PROJECT_CONFIG_OSD_BIGFONT_SUPPORT == HV_CONFIG_ON)
    if (pstCustom->usIsBigIconFont)
    {
        return Hv_Drv_OsdFont_DrawBigIconFontCustomText(pstRowInfo, pstNode, pstColumn, bIsUpdate);
    }
    else if (pstCustom->usIsBigBmpFont)
    {
        return Hv_Drv_OsdFont_DrawBigBmpFontCustomText(pstRowInfo, pstNode, pstColumn, bIsUpdate);
    }
#endif

    pstBlock = &g_astAllBlocks[pstCustom->ucBlockID];
    pucWidth = Hv_Drv_OsdResource_GetRealWidth(pstBlock);

    Hv_Drv_OsdResource_GetSize(pstBlock->usResourceSize, &ucWidth, NULL);

    if (0 == pstCustom->ucStringLength)
    {
        OSD_DRV_LOG_WARN("CP-%s AsciiString length is 0", CP_NAME(pstNode->stCp));
    }
    else if (pstCustom->ucStringLength > OSD_ASCII_STRING_LENGTH_MAX)
    {
        OSD_DRV_LOG_WARN("CP-%s AsciiString length %d exceeds max %d", CP_NAME(pstNode->stCp),
                pstCustom->ucStringLength, OSD_ASCII_STRING_LENGTH_MAX);

        pstCustom->ucStringLength = OSD_ASCII_STRING_LENGTH_MAX;
    }

    pucMatrixAddr = Hv_Drv_OsdResource_GetBlockMatrixAddr(pstBlock);
    for (ucIndex = 0; ucIndex < pstCustom->ucStringLength; ++ucIndex)
    {
        usCharOffset = Hv_Drv_OsdResource_SearchFontIndexByCode(pstCustom->ucBlockID,
                            pstCustom->ausString[ucIndex]);

        bReadOut = Hv_Drv_OsdShift_WriteCharacter(pstRowInfo, pucMatrixAddr, usCharOffset, pucWidth[usCharOffset]);

        if (HV_TRUE == bReadOut)
        {
            usSlot = Hv_Drv_OsdFont_TakeCurrentSlot(pstRowInfo, bIsUpdate);
            Hv_Cal_OsdIndex_SetColIndex(pstColumn, usSlot);
            Hv_Cal_OsdIndex_SetColType(pstColumn, OSD_COLUMN_FONT);
            Hv_Cal_OsdIndex_SetFontColColorIndex(pstColumn, pstCustom->ucColorIndex);

            OSD_DRV_LOG_DEBUG("Draw Custom String index %u colorIndex %d delta %d",
                    Hv_Cal_OsdIndex_GetColIndex(pstColumn),
                    pstCustom->ucColorIndex,
                    Hv_Cal_OsdIndex_GetColDelta(pstColumn));

            pstColumn++;
        }
    }
    ucRealWidth = Hv_Drv_OsdShift_EndTransaction(HV_FALSE);
    if (0 != ucRealWidth)
    {
        usSlot = Hv_Drv_OsdFont_TakeCurrentSlot(pstRowInfo, bIsUpdate);
        Hv_Cal_OsdIndex_SetColIndex(pstColumn, usSlot);
        Hv_Cal_OsdIndex_SetColType(pstColumn, OSD_COLUMN_FONT);
        Hv_Cal_OsdIndex_SetFontColColorIndex(pstColumn, pstCustom->ucColorIndex);
        Hv_Cal_OsdIndex_SetColDelta(pstColumn, HV_FALSE, ucWidth - ucRealWidth);

        pstColumn++;
    }

    return pstColumn;
}

static OsdIndexColumn* Hv_Drv_OsdFont_DrawCustomText(OsdRowInfo *pstRowInfo,
                            OsdComponentNode *pstNode, OsdIndexColumn *pstColumn,
                            BOOL bIsUpdate)
{
    OsdAsciiString *pstAscii = NULL;
#if (HV_PROJECT_CONFIG_OSD_MARQUEE == HV_CONFIG_ON)
    OsdMarqueeNode *pstMarquee = NULL;
#endif

#if (HV_PROJECT_CONFIG_OSD_TEXT_WRAPPING == HV_CONFIG_ON)
    pstAscii = (OsdAsciiString*)Hv_Drv_OsdFont_GetComponentData(pstNode);
    if (pstAscii->usIsLongText)
    {
        return Hv_Drv_OsdFont_DrawWrapText(pstRowInfo, pstNode, pstColumn, bIsUpdate);
    }
#endif

#if (HV_PROJECT_CONFIG_OSD_MARQUEE == HV_CONFIG_ON)
    if (Hv_Drv_OsdFont_GetMarqueeEnable(pstNode))
    {
        pstMarquee = pstNode->pstMarquee;
        if (NULL != pstMarquee)
        {
            pstMarquee->pstRowInfo = pstRowInfo;
            pstMarquee->pstColumn = pstColumn;

            return Hv_Drv_OsdFont_DrawMarqueeText(pstMarquee, bIsUpdate);
        }
        else
        {
            return pstColumn;
        }
    }
    else
    {
#endif
        return Hv_Drv_OsdFont_DrawNormalCustomText(pstRowInfo, pstNode, pstColumn, bIsUpdate);
#if (HV_PROJECT_CONFIG_OSD_MARQUEE == HV_CONFIG_ON)
    }
#endif
}

#if (HV_PROJECT_CONFIG_OSD_CUSTOM_GROUP_SUPPORT == HV_CONFIG_ON)
OsdCustomGroupInfoNode* Hv_Drv_OsdFont_SearchCustomGroup(OsdComponent stCp)
{
    struct LinkedList *pstLinkedNode = NULL;
    OsdCustomGroupInfoNode *pstCGInfoNode = NULL;

    Hv_Comm_LinkedList_ForEach(&s_pstFontParam->stAllCustomGroups, pstLinkedNode)
    {
        pstCGInfoNode = Hv_Comm_LinkedList_Entry(pstLinkedNode, OsdCustomGroupInfoNode, stEntry);

        if (CP_COMPARE(stCp, pstCGInfoNode->stCp))
        {
            return pstCGInfoNode;
        }
    }

    return NULL;
}

VOID Hv_Drv_OsdFont_RemoveCustomGroup(OsdComponent stCp)
{
    OsdCustomGroupInfoNode *pstCGInfoNode = NULL;

    pstCGInfoNode = Hv_Drv_OsdFont_SearchCustomGroup(stCp);
    if (NULL == pstCGInfoNode)
    {
        OSD_DRV_LOG_WARN("Osd WARN: could not find custom group info node of CP-%s", CP_NAME(stCp));
        return;
    }

    OSD_DRV_LOG_DEBUG("remove custom group of CP-%s", CP_NAME(stCp));

    Hv_Comm_LinkedList_Del(&s_pstFontParam->stAllCustomGroups, &pstCGInfoNode->stEntry);

    HV_FREE(pstCGInfoNode);
    return;
}

VOID Hv_Drv_OsdFont_PrepareCustomGroup(OsdComponent stCp, OsdCustomGroupInfo *pstCGInfo)
{
    UCHAR8 ucIndex = 0;
    USHORT16 usX = 0;
    OsdComponentNode *pstInnerNode = NULL;
    OsdCustomGroupInfoNode *pstCGInfoNode = NULL;

    /* calculate box for each component */
    usX = Hv_Drv_OsdFont_CalculateCustomGroupBox(pstCGInfo);

    for (ucIndex = 0; ucIndex < pstCGInfo->ucComponentCount; ++ucIndex)
    {
        pstInnerNode = pstCGInfo->apComponents[ucIndex];

        pstInnerNode->stBox.usX = usX;
        Hv_Drv_OsdFont_SetComponentX(pstInnerNode, usX);

        OSD_DRV_LOG_DEBUG("custom group component(%s) start x set to: %d", CP_NAME(pstInnerNode->stCp), usX);

        usX += pstInnerNode->stBox.usRealWidth;
        usX += pstCGInfo->aucGapWidth[ucIndex];
    }

    pstCGInfoNode = Hv_Drv_OsdFont_SearchCustomGroup(stCp);
    if (NULL == pstCGInfoNode)
    {
        pstCGInfoNode = HV_MALLOC(sizeof(*pstCGInfoNode));
        HV_MEMSET(pstCGInfoNode, 0, sizeof(*pstCGInfoNode));
        Hv_Comm_LinkedList_Init(&pstCGInfoNode->stEntry);

        Hv_Comm_LinkedList_AddTail(&s_pstFontParam->stAllCustomGroups, &pstCGInfoNode->stEntry);
        OSD_DRV_LOG_DEBUG("add new custom group CP-%s", CP_NAME(stCp));
    }
    else
    {
        OSD_DRV_LOG_DEBUG("found custom group CP-%s while preparing", CP_NAME(stCp));
    }

    pstCGInfoNode->stCp = stCp;
    HV_MEMCPY(&pstCGInfoNode->stCGInfo, pstCGInfo, sizeof(*pstCGInfo));

    return;
}

VOID Hv_Drv_OsdFont_RelocateAllCustomGroup(VOID)
{
    struct LinkedList *pstLinkedNode = NULL;
    struct LinkedList *pstNextNode = NULL;
    OsdCustomGroupInfoNode *pstCGInfoNode = NULL;

    Hv_Comm_LinkedList_ForEachSafe(&s_pstFontParam->stAllCustomGroups, pstNextNode, pstLinkedNode)
    {
        pstCGInfoNode = Hv_Comm_LinkedList_Entry(pstLinkedNode, OsdCustomGroupInfoNode, stEntry);

        OSD_DRV_LOG_DEBUG("relocate custom group CP-%s", CP_NAME(pstCGInfoNode->stCp));
        Hv_Drv_OsdFont_PrepareCustomGroup(pstCGInfoNode->stCp, &pstCGInfoNode->stCGInfo);
    }

    return;
}

#endif

static VOID Hv_Drv_OsdFont_ClearRow(OsdRowInfo *pstRowInfo)
{
    HV_MEMSET((VOID*)pstRowInfo->pucColumnStart, 0,
           OSD_INDEX_COLUMN_SIZE * pstRowInfo->ucRowLen);
}

static VOID Hv_Drv_OsdFont_DrawBlankRow(OsdRowInfo *pstRowInfo)
{
    UCHAR8 ucIndex = 0;
    UCHAR8 ucBlankWidth = 0;
    OsdIndexColumn *pstColumn = NULL;

    ucBlankWidth = pstRowInfo->ucBlankWidth;
    if (ucBlankWidth < HV_OSD_CONFIG_COLUMN_MINWIDTH)
    {
        ucBlankWidth = HV_OSD_CONFIG_COLUMN_MINWIDTH;
    }

    pstColumn = (OsdIndexColumn *)pstRowInfo->pucColumnStart;
    for (ucIndex = 0; ucIndex < pstRowInfo->ucRowLen; ++ucIndex)
    {
        Hv_Cal_OsdIndex_SetColType(pstColumn + ucIndex, OSD_COLUMN_BLANK);
        Hv_Cal_OsdIndex_SetColBlankWidth(pstColumn + ucIndex, ucBlankWidth);
    }
}

static BOOL Hv_Drv_OsdFont_DetermineLoadedBank(OsdRowInfo *pstRowInfo, BOOL bIsUpdate, BOOL *pbUseUpdateRegion)
{
    OsdPhyFontInfo *pstFontInfo = NULL;
    OsdRowCatalog *pstCatalog = NULL;
    OsdRadixTree *pstTree = NULL;
    USHORT16 usAvailSlots = 0;
    USHORT16 usLeftAvailSlots = 0;
    USHORT16 usRightAvailSlots = 0;
    UCHAR8 ucSlotsNeed = 0;
    BOOL bSatisfy = HV_FALSE;
    BOOL bRes = HV_FALSE;

    *pbUseUpdateRegion = HV_FALSE;

    pstFontInfo = Hv_Drv_OsdFont_GetCurrentFontInfo();
#if (HV_OSD_CONFIG_RESERVE_SLOTS_BYROW == HV_CONFIG_ON)
    if (!bIsUpdate)
    {
#if (HV_OSD_CONFIG_FONT_AS_BACKGROUND == HV_CONFIG_ON)
        if (!pstFontInfo->bIsBackground)
        {
#endif
            ucSlotsNeed = pstRowInfo->ucResourceLen;

            pstRowInfo->usResourceOffset = Hv_Drv_OsdResource_PickLoadedBank(
                    pstRowInfo->enSize, pstRowInfo->enType, ucSlotsNeed);
            pstRowInfo->pstLoadedBank = Hv_Drv_OsdResource_GetCurrentLoadedBank();
            pstRowInfo->usBankSlot
                = pstRowInfo->pstLoadedBank->usCurrentSlots - ucSlotsNeed;
#if (HV_OSD_CONFIG_FONT_AS_BACKGROUND == HV_CONFIG_ON)
        }
#endif
        pstRowInfo->ucCurIndex = 0;
    }
    else /* 更新、隐藏的时候不需要分配bank空间*/
    {
        pstRowInfo->ucCurIndex = 0;
    }
#else
    if (!bIsUpdate)
    {
        /* set loaded bank */
        pstRowInfo->pstLoadedBank = Hv_Drv_OsdResource_GetCurrentLoadedBank();
    }
    else
    {
        pstCatalog = Hv_Drv_OsdFont_GetCurrentCatalog();
        HV_ASSERT_TRUE_RET((pstCatalog != NULL) && (pstCatalog->pstSlotsManager != NULL), HV_FAILURE);

        /* check if redraw region satisfy update? */
        ucSlotsNeed = pstRowInfo->ucResourceCount;
#if (HV_PROJECT_CONFIG_OSD_CPU_CANVAS_SUPPORT == HV_CONFIG_ON)
        ucSlotsNeed -= pstRowInfo->ucCPUCanvasColumnCount;
#endif
        pstTree = pstCatalog->pstSlotsManager;
        usAvailSlots = Hv_Drv_OsdRadix_GetEmptySlots(pstTree);
        /* if slots exceed 512, need to check left tree and right tree alone */
        if (pstTree->usTotalSlots <= OSD_BANK_CAPACITY)
        {
            bSatisfy = (ucSlotsNeed <= usAvailSlots);
        }
        else
        {
            usLeftAvailSlots = Hv_Drv_OsdRadix_GetLeftEmptySlots(pstTree);
            usRightAvailSlots = Hv_Drv_OsdRadix_GetRightEmptySlots(pstTree);

            bSatisfy = (ucSlotsNeed <= usLeftAvailSlots) || (ucSlotsNeed <= usRightAvailSlots);
        }

        if (!bSatisfy)
        {
            OSD_DRV_LOG_DEBUG("redraw region empty slots: %d, row need %d. "
                    "need to allocate from update region", usAvailSlots,
                    pstRowInfo->ucResourceCount);

            /* check if update manager created? */
            if (NULL == pstCatalog->pstUpdateManager)
            {
                pstCatalog->pstUpdateManager =
                    Hv_Drv_OsdFont_CreateUpdateManager(pstCatalog, ucSlotsNeed);

                HV_ASSERT_TRUE_RET(pstCatalog->pstUpdateManager != NULL, HV_FAILURE);
            }
            else
            {
                pstTree = pstCatalog->pstUpdateManager->pstUpdateSlotsManager;
                usAvailSlots = Hv_Drv_OsdRadix_GetEmptySlots(pstTree);

                /* check if need to expand update region? */
                if (ucSlotsNeed > usAvailSlots)
                {
                    OSD_DRV_LOG_DEBUG("update region empty slots: %d, row need %d. need to expand",
                            usAvailSlots, ucSlotsNeed);

                    bRes = Hv_Drv_OsdFont_ExpandUpdateRegion(pstCatalog->pstUpdateManager, ucSlotsNeed);
                    if (HV_FAILURE == bRes)
                    {
                        return HV_FAILURE;
                    }
                }
                else
                {
                    OSD_DRV_LOG_DEBUG("update region empty slots: %d, row need %d. satisfy",
                            usAvailSlots, ucSlotsNeed);
                }
            }

            pstRowInfo->pstLoadedBank = pstCatalog->pstUpdateManager->pstUpdateBank;

            *pbUseUpdateRegion = HV_TRUE;
        }
        else
        {
            OSD_DRV_LOG_DEBUG("redraw region empty slots: %d, row need %d. satisfy",
                    usAvailSlots, pstRowInfo->ucResourceCount);

            /* load balance: pick the more empty bank */
            if (Hv_Drv_OsdRadix_IsLeftLeaned(pstTree))
            {
                OSD_DRV_LOG_DEBUG("alloc slots from left child");
                pstRowInfo->pstLoadedBank = pstCatalog->pstCatalogBank;
            }
            else
            {
                OSD_DRV_LOG_DEBUG("alloc slots from right child");
                pstRowInfo->pstLoadedBank = pstCatalog->pstCatalogBank + 1;
            }
        }
    }
#endif

    return HV_SUCCESS;
}

/**
 * Redraw one row
 */
static VOID Hv_Drv_OsdFont_DrawOneRow(OsdRowInfo *pstRowInfo, BOOL bIsUpdate)
{
    UCHAR8 ucIndex = 0;
    UCHAR8 ucWidth = 0;
    UCHAR8 ucExtraPixel = 0;
    UCHAR8 ucRmd = 0;
    USHORT16 usX = 0;
    USHORT16 usLastX = 0;
    USHORT16 usGap = 0;
    UCHAR8 aucCPName[32] = {0};
    OsdComponentNode *pstComponent = NULL;
    OsdComponentNode *pstLastComponent = NULL;
    OsdResourceType enType = 0;
    OsdLoadedBank *pstLoadedBank = NULL;
    OsdIndexRow *pstRow = NULL;
    OsdIndexColumn *pstColumn = NULL;
    OsdPhyFontInfo *pstFontInfo = NULL;
    BOOL bUseUpdateRegion = HV_FALSE;
    BOOL bFillGap = HV_TRUE;
    const pfDrawComponentFunc ppDrawFuncs[OSD_CP_TYPE_MAX] = {
        [OSD_CP_TYPE_TEXT] = Hv_Drv_OsdFont_DrawText,
        [OSD_CP_TYPE_TEXT_LIST] = Hv_Drv_OsdFont_DrawTextList,
        [OSD_CP_TYPE_NUMBER] = Hv_Drv_OsdFont_DrawNumber,
        [OSD_CP_TYPE_ASCII_STRING] = Hv_Drv_OsdFont_DrawCustomText,
        [OSD_CP_TYPE_ICON] = Hv_Drv_OsdFont_DrawIcon,
        [OSD_CP_TYPE_ICON_LIST] = Hv_Drv_OsdFont_DrawIconList,
        [OSD_CP_TYPE_BMP] = Hv_Drv_OsdFont_DrawBMP,
        [OSD_CP_TYPE_BMP_LIST] = Hv_Drv_OsdFont_DrawBmpList,
    };

    OSD_DRV_LOG_DEBUG("Begin to draw row %d usY %d components %u",
            pstRowInfo->ucRowIndex, pstRowInfo->usY, pstRowInfo->ucComponentCount);

    pstFontInfo = Hv_Drv_OsdFont_GetCurrentFontInfo();

    Hv_Drv_OsdResource_GetSize(pstRowInfo->enSize, &ucWidth, NULL);

    Hv_Drv_OsdFont_ClearRow(pstRowInfo);

    pstRowInfo->ucBlankWidth =
        Hv_Drv_OsdFont_CalculateAvgBlankWidth(pstFontInfo->usFontAreaWidth, pstRowInfo);

    Hv_Drv_OsdFont_DrawBlankRow(pstRowInfo);

    /*
     * 重新绘制需要分配bank空间
     */
    if (HV_FAILURE == Hv_Drv_OsdFont_DetermineLoadedBank(pstRowInfo, bIsUpdate, &bUseUpdateRegion))
    {
        OSD_DRV_LOG_ERROR("osd fail to determine row bank, quit row drawing");
        return;
    }

    Hv_Drv_OsdShift_InitTransaction(pstRowInfo, bIsUpdate, bUseUpdateRegion);

    pstColumn = (OsdIndexColumn*)pstRowInfo->pucColumnStart;
    for (ucIndex = 0; ucIndex < pstRowInfo->ucComponentCount; ++ucIndex)
    {
        pstComponent = pstRowInfo->apCurrentComponents[ucIndex];
        Hv_Drv_OsdFont_LogComponent(pstComponent);

        bFillGap = HV_TRUE;

        enType = Hv_Drv_OsdFont_GetComponentResourceType(pstComponent);
        if (enType != pstRowInfo->enType)
        {
            OSD_DRV_LOG_ERROR("Osd fatal: forbid drawing different bit components at the same line, CP-%s %s ROW is %s",
                    CP_NAME(pstComponent->stCp),
                    Hv_Drv_OsdCommon_GetResourceTypeStr(enType),
                    Hv_Drv_OsdCommon_GetResourceTypeStr(pstRowInfo->enType));
        }

        usX = pstComponent->stBox.usX;

        if ((usLastX == 0) && ((usX != 0) && (usX < HV_OSD_CONFIG_COLUMN_MINWIDTH)))
        {
            if (Hv_Drv_OsdFont_ComponentIsBmpOrIcon(pstComponent))
            {
                OSD_DRV_LOG_ERROR("OSD forbids Icon/BMP CP-%s draws at %d", CP_NAME(pstComponent->stCp), usX);
                continue;
            }

            Hv_Drv_OsdShift_WriteBlank(usX);
            bFillGap = HV_FALSE;
        }

        if (usX < usLastX)
        {
            strcpy(aucCPName, CP_NAME(pstComponent->stCp));

            OSD_DRV_LOG_WARN("%d %d: CP-%s @(%d, %d) overlap with previous CP-%s @(%d, %d, %d), skip", usX, usLastX,
                            aucCPName, pstComponent->stBox.usX, pstComponent->stBox.usY,
                            CP_NAME(pstLastComponent->stCp), pstLastComponent->stBox.usX, pstLastComponent->stBox.usY,
                            pstLastComponent->stBox.usRealWidth);
            continue;
        }

        if (bFillGap)
        {
            /*                 __ usLastX
             *                /
             * -------------------    -----------------
             * | comp-0       |//|    | comp-1
             * -------------------    -----------------
             * |   RealWidth  |Extra   \
             * |<-    usW     ->|       ---usX
             *
             */
            usGap = usX - usLastX;
            if (NULL != pstLastComponent)
            {
                ucExtraPixel = pstLastComponent->stBox.usW - pstLastComponent->stBox.usRealWidth;
                if (usGap < ucExtraPixel)
                {
                    OSD_DRV_LOG_WARN("Row-%d components draws %d pixels backwards starting from CP-%s",
                            pstRowInfo->usY, HV_OSD_CONFIG_COLUMN_MINWIDTH - usGap, CP_NAME(pstComponent->stCp));

                    usGap = 0;
                }
                else
                {
                    usGap -= ucExtraPixel;
                }
            }
            pstColumn = Hv_Drv_OsdIndex_FillColumnBlanks(pstColumn, usGap, pstRowInfo->ucBlankWidth);
        }

        usLastX = usX + pstComponent->stBox.usRealWidth;

        if (ppDrawFuncs[pstComponent->stCp.enType])
        {
            pstColumn = ppDrawFuncs[pstComponent->stCp.enType](pstRowInfo, pstComponent, pstColumn, bIsUpdate);

            pstLastComponent = pstComponent;
        }
        else
        {
            OSD_DRV_LOG_ERROR("osd fatal error: not supported component type: %d",
                    pstComponent->stCp.enType);
        }
    }

    /* 设置row类型 */
#if (HV_OSD_CONFIG_FONT_AS_BACKGROUND == HV_CONFIG_ON)
    if (pstFontInfo->bIsBackground)
    {
        pstLoadedBank = Hv_Drv_OsdResource_GetCurrentLoadedBank();
    }
    else
    {
#endif
        pstLoadedBank = pstRowInfo->pstLoadedBank;
#if (HV_OSD_CONFIG_FONT_AS_BACKGROUND == HV_CONFIG_ON)
    }
#endif

    pstRow = Hv_Drv_OsdIndex_GetRow(pstRowInfo->ucRowIndex);
    if (!bIsUpdate)
    {
        Hv_Cal_OsdIndex_SetRowFontLibIndex(pstRow,
                Hv_Drv_OsdResource_FindResource(pstLoadedBank->ucBankID, HV_TRUE));
        Hv_Cal_OsdIndex_SetRowFontType(pstRow, pstRowInfo->enType);
        Hv_Cal_OsdIndex_SetRowLen(pstRow, pstRowInfo->ucRowLen);
    }
#if (HV_OSD_CONFIG_RESERVE_SLOTS_BYROW == HV_CONFIG_OFF)
    else
    {
        Hv_Cal_OsdIndex_SetRowFontLibIndex(pstRow,
                Hv_Drv_OsdResource_FindResource(pstLoadedBank->ucBankID, HV_TRUE));
    }
#endif

    OSD_DRV_LOG_DEBUG("End of row draw");
}

static VOID Hv_Drv_OsdFont_SetResource(VOID)
{
    OsdPhyFontInfo *pstFontInfo = NULL;
    OsdLoadedBank *pstLoadedBank = NULL;
    OsdResourceSize enSize = 0;
    UCHAR8 ucWidth = 0;
    UCHAR8 ucHeight = 0;
    UCHAR8 ucIndex = 0;

    pstFontInfo = Hv_Drv_OsdFont_GetCurrentFontInfo();

    for (ucIndex = 0; ucIndex < pstFontInfo->ucResourceCount; ++ucIndex)
    {
        pstLoadedBank = pstFontInfo->apResources[ucIndex];

        enSize = Hv_Drv_OsdResource_ConvertSize(pstLoadedBank->enSize);
        Hv_Cal_OsdWindow_SetResource(ucIndex,
                pstLoadedBank->usCurrentResourceOffset, enSize);
        /* set user-defined font type */
        if (enSize != pstLoadedBank->enSize)
        {
            Hv_Drv_OsdResource_GetSize(pstLoadedBank->enSize, &ucWidth, &ucHeight);
            Hv_Cal_OsdWindow_SetUserDefinedFont(pstLoadedBank->enType, ucWidth, ucHeight);

            OSD_DRV_LOG_DEBUG("Osd set userdefined font: %s %s",
                    Hv_Drv_OsdCommon_GetResourceSizeStr(pstLoadedBank->enSize),
                    Hv_Drv_OsdCommon_GetResourceTypeStr(pstLoadedBank->enSize));
        }
    }
}

static VOID Hv_Drv_OsdFont_ReInitializeIndex(OsdIndexInfo *pstIndexInfo)
{
    struct LinkedList *pstNode = NULL;
    struct LinkedList *pstNext = NULL;
    OsdRowInfo *pstRowInfo = NULL;

    Hv_Comm_LinkedList_ForEachSafe(&pstIndexInfo->stRowInfoList, pstNext, pstNode)
    {
        pstRowInfo = Hv_Comm_LinkedList_Entry(pstNode, OsdRowInfo, stEntry);

        Hv_Comm_LinkedList_Del(&pstIndexInfo->stRowInfoList, pstNode);
        Hv_Drv_OsdFont_FreeRowInfo(pstRowInfo);
    }

    HV_MEMSET(pstIndexInfo, 0, sizeof(*pstIndexInfo));
    Hv_Comm_LinkedList_Init(&pstIndexInfo->stRowInfoList);
}



#if (HV_OSD_CONFIG_RESERVE_SLOTS_BYROW == HV_CONFIG_ON)
BOOL Hv_Drv_OsdFont_FindCatalog(OsdResourceSize enSize, OsdResourceType enType,
                                BOOL bInsert, OsdRowCatalog **ppCatalogOut)
{
    OsdRowCatalog *pstCatalog = NULL;
    UCHAR8 ucIndex = 0;
    BOOL bFound = HV_FALSE;

    for (ucIndex = 0; ucIndex < s_pstFontParam->ucCatalogCount; ++ucIndex)
    {
        pstCatalog = &s_pstFontParam->astRowCatalogs[ucIndex];

        if ((pstCatalog->enSize == enSize) && (pstCatalog->enType == enType))
        {
            bFound = HV_TRUE;
            break;
        }
    }

    if (bInsert && !bFound)
    {
        HV_ASSERT_TRUE_RET(s_pstFontParam->ucCatalogCount < OSD_WINDOW_MAX_RESOURCE_COUNT, HV_FALSE);

        pstCatalog = &s_pstFontParam->astRowCatalogs[s_pstFontParam->ucCatalogCount++];
        pstCatalog->enSize = enSize;
        pstCatalog->enType = enType;
        Hv_Comm_LinkedList_Init(&pstCatalog->stRowInfos);

        bFound = HV_TRUE;
        OSD_DRV_LOG_DEBUG("new row catalog(%s %s) created, index: %u",
                Hv_Drv_OsdCommon_GetResourceSizeStr(enSize),
                Hv_Drv_OsdCommon_GetResourceTypeStr(enType),
                s_pstFontParam->ucCatalogCount - 1);
    }

    *ppCatalogOut = pstCatalog;
    return bFound;
}

#else

BOOL Hv_Drv_OsdFont_FindCatalog(OsdResourceSize enSize, OsdResourceType enType,
                                BOOL bInsert, OsdRowCatalog **ppCatalogOut)
{
    OsdPhyFontInfo *pstFontInfo = NULL;
    OsdRowCatalog *pstCatalog = NULL;
    OsdRowCatalog **ppCatalog = NULL;
    BOOL bFound = HV_FALSE;

    pstFontInfo = Hv_Drv_OsdFont_GetCurrentFontInfo();

    ppCatalog = &pstFontInfo->pstCatalogs;
    while ((pstCatalog = *ppCatalog) != NULL)
    {
        if ((pstCatalog->enSize == enSize) && (pstCatalog->enType == enType))
        {
            bFound = HV_TRUE;
            break;
        }

        ppCatalog = &pstCatalog->pstNext;
    }

    if (bInsert && !bFound)
    {
        pstCatalog = (OsdRowCatalog*)HV_MALLOC(sizeof(*pstCatalog));
        HV_ASSERT_TRUE_RET(pstCatalog != NULL, HV_FALSE);
        HV_MEMSET(pstCatalog, 0, sizeof(*pstCatalog));

        pstCatalog->enSize = enSize;
        pstCatalog->enType = enType;
        pstCatalog->ucWindowId = Hv_Drv_OsdWindow_GetCurrentWindow();
        Hv_Comm_LinkedList_Init(&pstCatalog->stRowInfos);

        bFound = HV_TRUE;
        *ppCatalog = pstCatalog;
        OSD_DRV_LOG_DEBUG("new row catalog(%s %s) created",
                Hv_Drv_OsdCommon_GetResourceSizeStr(enSize),
                Hv_Drv_OsdCommon_GetResourceTypeStr(enType));
    }

    *ppCatalogOut = pstCatalog;
    return bFound;
}
#endif

/**
 * @brief append rowInfo to catalog, no need to sort here
 * @param[in] pstRowInfo row structure
 * @return VOID without return
 */
static VOID Hv_Drv_OsdFont_InsertRowToCatalog(OsdPhyFontInfo *pstFontInfo, OsdRowInfo *pstRowInfo)
{
    OsdRowCatalog *pstCatalog = NULL;
    OsdRowCatalogNode *pstNode = NULL;
    OsdRowCatalogNode *pstTmpNode = NULL;
    struct LinkedList *pstLinkedNode = NULL;
    BOOL bFound = HV_FALSE;

    if (pstRowInfo->usAddedToCatalog)
    {
        OSD_DRV_LOG_DEBUG("row %u has been added to catalog", pstRowInfo->usY);
        return;
    }

    Hv_Drv_OsdFont_FindCatalog(pstRowInfo->enSize, pstRowInfo->enType, HV_TRUE, &pstCatalog);
    if (NULL == pstCatalog)
    {
        OSD_DRV_LOG_ERROR("osd fatal error, could not create catalog for ROW %d", pstRowInfo->usY);
        return;
    }

    pstNode = (OsdRowCatalogNode*)OSD_DRIVER_ALLOC_STATIC(OsdRowCatalogNode, 1);
    pstNode->pstRowInfo = pstRowInfo;
    Hv_Comm_LinkedList_Init(&pstNode->stNode);

    /* wrap text requires Y-ordering draw */
    Hv_Comm_LinkedList_ForEach(&pstCatalog->stRowInfos, pstLinkedNode)
    {
        pstTmpNode = Hv_Comm_LinkedList_Entry(pstLinkedNode, OsdRowCatalogNode, stNode);

        if (pstTmpNode->pstRowInfo->usY > pstRowInfo->usY)
        {
            break;
        }
    }
    Hv_Comm_LinkedList_AddBefore(&pstCatalog->stRowInfos, pstLinkedNode, &pstNode->stNode);

    pstRowInfo->usAddedToCatalog = HV_TRUE;
#if (HV_OSD_CONFIG_RESERVE_SLOTS_BYROW == HV_CONFIG_OFF)
    pstRowInfo->pstCatalog = pstCatalog;
#endif
}

static VOID Hv_Drv_OsdFont_PrepareRedrawing(OsdWindow *pstWindow, OsdPhyFontInfo *pstFontInfo)
{
    OsdIndexInfo *pstIndex = &pstFontInfo->stIndexInfo;
    const OsdWindowProp *pstProp = NULL;

#if (HV_PROJECT_CONFIG_OSD_MARQUEE == HV_CONFIG_ON)
    /* malloc slots at next draw */
    //Hv_Drv_OsdFont_MarqueeMarkSlotsFreed(pstWindow->ucPhyId);
#endif

    Hv_Drv_OsdFont_ReInitRowCatalogs(pstFontInfo);

    /* 释放rowinfo */
    Hv_Drv_OsdFont_ReInitializeIndex(pstIndex);
    pstIndex->pucIndexDDRAddr = Hv_Drv_OsdIndex_GetCurrentAddress();

    pstProp = OSD_GET_WINDOW_PROP(pstWindow->ucId);
    pstFontInfo->bIsBackground = pstProp->bIsBackground;
    pstFontInfo->usFontAreaWidth = pstWindow->usFontAreaWidth;
    pstFontInfo->ucBlankBankIndex = 0xff;
    pstFontInfo->ucResourceCount = 0;
}

static Status Hv_Drv_OsdFont_AssignComponent(OsdPhyFontInfo *pstFontInfo, OsdComponentNode *pstRealComponent, BOOL bCalculateBox)
{
    UCHAR8 ucIndex = 0;
    UCHAR8 ucWidth = 0;
    UCHAR8 ucHeight = 0;
    UCHAR8 ucRows = 0;
    UCHAR8 ucCurRow = 0;
    USHORT16 usY = 0;
    const OsdBlock *pstBlock = NULL;
    OsdRowInfo *pstRowInfo = NULL;
    OsdRowInfo *pstPrevRowInfo = NULL;
    struct LinkedList *pstNode = NULL;
    OsdIndexInfo *pstIndex = &pstFontInfo->stIndexInfo;

    pstBlock = Hv_Drv_OsdFont_GetBlock(pstRealComponent);
    if (NULL == pstBlock)
    {
        OSD_DRV_LOG_ERROR("OSD fatal error: could not decide CP-%s", CP_NAME(pstRealComponent->stCp));

        return HV_FAILURE;
    }

    /* rowinfo指向component画到的第一行*/
    pstRealComponent->pstRowInfo = NULL;

    Hv_Drv_OsdResource_GetSize(pstBlock->usResourceSize, &ucWidth, &ucHeight);

    ucRows = Hv_Drv_OsdFont_GetComponentRows(pstRealComponent);
    usY = Hv_Drv_OsdFont_GetComponentY(pstRealComponent);

    pstPrevRowInfo = NULL;
    for (ucCurRow = 0; ucCurRow < ucRows; ++ucCurRow)
    {
        if (NULL == pstPrevRowInfo)
        {
            pstRowInfo = Hv_Drv_OsdFont_GetRowInfoByYaxis(usY, HV_TRUE);
            if (NULL == pstRowInfo)
            {
                OSD_DRV_LOG_ERROR("CP-%s X %d Y %d would not be drawn",
                        CP_NAME(pstRealComponent->stCp), Hv_Drv_OsdFont_GetComponentX(pstRealComponent), usY);

                break;
            }
        }
        else
        {
            pstNode = pstPrevRowInfo->stEntry.next;
            pstRowInfo = Hv_Comm_LinkedList_Entry(pstNode, OsdRowInfo, stEntry);

            if (pstNode != &pstIndex->stRowInfoList)
            {
                /* usY不能有重叠 */
                if ((pstRowInfo->usY != usY) && (usY + ucHeight > pstRowInfo->usY))
                {
                    OSD_DRV_LOG_ERROR("normal rows overlap: current Y %u "
                            "current height %u next Y %u", usY, ucHeight, pstRowInfo->usY);
                    usY += ucHeight;
                    pstPrevRowInfo = pstRowInfo;
                    continue;
                }
            }

            if ((pstNode == &pstIndex->stRowInfoList) || (pstRowInfo->usY != usY))
            {
                pstRowInfo = Hv_Drv_OsdFont_AllocRowInfo();
                pstRowInfo->usY = usY;

                Hv_Comm_LinkedList_AddAfter(&pstIndex->stRowInfoList,
                        &pstPrevRowInfo->stEntry, &pstRowInfo->stEntry);
            }
        }

        pstRowInfo->enSize = pstBlock->usResourceSize;
        pstRowInfo->enType = pstBlock->ucResourceType;
        pstRowInfo->ucBankID = pstBlock->ucBankID;

        pstPrevRowInfo = pstRowInfo;

        Hv_Drv_OsdFont_InsertRowToCatalog(pstFontInfo, pstRowInfo);

        if (0 == ucCurRow)
        {
            if (bCalculateBox)
            {
                Hv_Drv_OsdFont_CalculateComponentBox(pstRealComponent, HV_FALSE);
            }
            pstRealComponent->pstRowInfo = pstRowInfo;
        }

        Hv_Drv_OsdFont_RowInfoInsertComponent(pstRowInfo, pstRealComponent, HV_FALSE);

        usY += ucHeight;
#if (HV_PROJECT_CONFIG_OSD_TEXT_WRAPPING == HV_CONFIG_ON)
        usY += Hv_Drv_OsdFont_GetWrapSpace(pstRealComponent);
#endif
    }

    return HV_SUCCESS;
}

static Status Hv_Drv_OsdFont_SortComponents(OsdPhyFontInfo *pstFontInfo, OsdNodeHead *pstHead)
{
    OsdBox stBox;
    USHORT16 usWidth = 0;
    HV_ASSERT_VALID_PTR(pstHead);
    OsdComponentNode *pstCur = pstHead->pstNode;
    USHORT16 usUpdateNum = pstHead->usUpdateNum;

    while (NULL != pstCur)
    {
        Hv_Drv_OsdFont_AssignComponent(pstFontInfo, pstCur, HV_TRUE);

#if (HV_PROJECT_CONFIG_OSD_ROTATION == HV_CONFIG_ON)
        if (usUpdateNum > 0)
        {
            stBox = pstCur->stBox;
            usWidth = Hv_Drv_OsdFont_GetComponentWidth(pstCur);
            if (usWidth > 0)
            {
                stBox.usW = usWidth;
            }
            stBox.usX = Hv_Drv_OsdFont_GetComponentX(pstCur);
            OSD_DRV_LOG_DEBUG("Prepare rotate CP-%s", CP_NAME(pstCur->stCp));
            Hv_Drv_OsdAppendDrawAreaBox(stBox);
            usUpdateNum--;
        }
#endif

        pstCur = pstCur->pstNext;
    }

    return HV_SUCCESS;
}

static VOID Hv_Drv_OsdFont_AllocIndexRows(OsdPhyFontInfo *pstFontInfo)
{
    UCHAR8 ucIndex = 0;
    UCHAR8 ucWidth = 0;
    UCHAR8 ucHeight = 0;
    USHORT16 usTotalFontWidth = 0;
    USHORT16 usY = 0;
    USHORT16 usNextY = 0;
    BOOL bStartYSet = HV_FALSE;
    OsdIndexRow *pstRow = NULL;
    OsdRowInfo *pstRowInfo = NULL;
    OsdRowInfo *pstPrevRowInfo = NULL;
    OsdComponentNode *pstComponent = NULL;
    struct LinkedList *pstNode = NULL;
    struct LinkedList *pstNext = NULL;
    OsdIndexInfo *pstIndex = &pstFontInfo->stIndexInfo;

    /* 分配row index，插入blank row */
    Hv_Comm_LinkedList_ForEachSafe(&pstIndex->stRowInfoList, pstNext, pstNode)
    {
        pstPrevRowInfo = Hv_Comm_LinkedList_Entry(pstNode, OsdRowInfo, stEntry);
        pstRowInfo = Hv_Comm_LinkedList_Entry(pstNext, OsdRowInfo, stEntry);

        usY = pstPrevRowInfo->usY;
        if (pstNext != &pstIndex->stRowInfoList)
        {
            usNextY = pstRowInfo->usY;
        }
        else
        {
            /* current row is the last row */
            usNextY = 0;
        }

        Hv_Drv_OsdResource_GetSize(pstPrevRowInfo->enSize, &ucWidth, &ucHeight);

        if (!bStartYSet)
        {
            Hv_Drv_OsdFont_SetFontStart(0, pstPrevRowInfo->usY);
            bStartYSet = HV_TRUE;

            pstRow = Hv_Drv_OsdIndex_NewRow(NULL);
        }
        else
        {
            pstRow = Hv_Drv_OsdIndex_NewRow(pstRow);
        }

        pstPrevRowInfo->ucRowIndex = pstIndex->ucRowCount - 1;

        pstPrevRowInfo->ucRowLen = roundup_div(pstFontInfo->usFontAreaWidth, ucWidth);
#if (HV_OSD_CONFIG_RESERVE_SLOTS_BYROW == HV_CONFIG_ON)
        pstPrevRowInfo->ucResourceLen = pstPrevRowInfo->ucRowLen;
#endif

        for (ucIndex = 0; ucIndex < pstPrevRowInfo->ucComponentCount; ++ucIndex)
        {
            pstComponent = pstPrevRowInfo->apCurrentComponents[ucIndex];

            usTotalFontWidth += pstComponent->stBox.usW;
        }

#if (HV_OSD_CONFIG_RESERVE_SLOTS_BYROW == HV_CONFIG_ON)
        if (usTotalFontWidth <
                (pstFontInfo->usFontAreaWidth * HV_OSD_CONFIG_ROW_RESOURCE_THRESHOLD))
        {
            pstPrevRowInfo->ucResourceLen *= HV_OSD_CONFIG_ROW_RESOURCE_COUNT_RATIO;
        }
#endif

        pstPrevRowInfo->ucRowLen += HV_OSD_CONFIG_BLANK_COLUMN_MARGIN;

#if (HV_OSD_CONFIG_RESERVE_SLOTS_BYROW == HV_CONFIG_ON)
        OSD_DRV_LOG_DEBUG("row %d resource count: %d index count: %d",
                pstPrevRowInfo->usY, pstPrevRowInfo->ucResourceLen,
                pstPrevRowInfo->ucRowLen);
#else
        OSD_DRV_LOG_DEBUG("row %d index count: %d",
                pstPrevRowInfo->usY, pstPrevRowInfo->ucRowLen);
#endif

        if (0 != usNextY)
        {
            pstRow = Hv_Drv_OsdIndex_FillBlankLines(pstRow, usNextY - (usY + ucHeight));
        }
    }

    /* 设置最后一行为end row*/
    Hv_Drv_OsdIndex_NewEndRow();
}

static VOID Hv_Drv_OsdFont_FillIndexColumns(OsdPhyFontInfo *pstFontInfo)
{
    USHORT16 usColumnOffset = 0;
    UCHAR8 *pucCurColumn = NULL;
    OsdRowInfo *pstRowInfo = NULL;
    struct LinkedList *pstNode = NULL;
    OsdIndexInfo *pstIndex = &pstFontInfo->stIndexInfo;
    OsdRowCatalog *pstCatalog = NULL;
    OsdRowCatalogNode *pstCatalogNode = NULL;
    OsdLoadedBank *pstLoadedBank = NULL;
    OsdLoadedBank *pstPostBank = NULL;
    UCHAR8 ucIndex = 0;
    UCHAR8 ucSlotIndex = 0;
    USHORT16 usBytes = 0;

    usColumnOffset = ALIGN(pstIndex->ucRowCount * OSD_INDEX_ROW_SIZE, 16);
    OSD_DRV_LOG_DEBUG("Row number %d Column offset %u", pstIndex->ucRowCount, usColumnOffset);

    pucCurColumn = pstIndex->pucIndexDDRAddr + usColumnOffset;
    Hv_Comm_LinkedList_ForEach(&pstIndex->stRowInfoList, pstNode)
    {
        pstRowInfo = Hv_Comm_LinkedList_Entry(pstNode, OsdRowInfo, stEntry);
        pstRowInfo->pucColumnStart = pucCurColumn;

        pucCurColumn += pstRowInfo->ucRowLen * OSD_INDEX_COLUMN_SIZE;
    }

    /* 按照资源分类依次绘制 */
#if (HV_OSD_CONFIG_RESERVE_SLOTS_BYROW == HV_CONFIG_ON)
    for (ucIndex = 0; ucIndex < s_pstFontParam->ucCatalogCount; ++ucIndex)
#else
    for (pstCatalog = pstFontInfo->pstCatalogs; pstCatalog != NULL; pstCatalog = pstCatalog->pstNext)
#endif
    {
#if (HV_OSD_CONFIG_RESERVE_SLOTS_BYROW == HV_CONFIG_ON)
        pstCatalog = &s_pstFontParam->astRowCatalogs[ucIndex];
#endif

        pstLoadedBank = Hv_Drv_OsdResource_NewLoadedBank(pstCatalog->enSize, pstCatalog->enType);

#if (HV_OSD_CONFIG_RESERVE_SLOTS_BYROW == HV_CONFIG_OFF)
        Hv_Drv_OsdFont_SetCurrentCatalog(pstCatalog);
        pstCatalog->pstCatalogBank = pstLoadedBank;
        pstCatalog->usResourceOffset = pstLoadedBank->usCurrentResourceOffset;
#endif

        Hv_Comm_LinkedList_ForEach(&pstCatalog->stRowInfos, pstNode)
        {
            pstCatalogNode = Hv_Comm_LinkedList_Entry(pstNode, OsdRowCatalogNode, stNode);
            pstRowInfo = pstCatalogNode->pstRowInfo;

            OSD_DRV_LOG_DEBUG("Draw Row %d Y %u columns %d "
                    " component count %d column addr 0x%x", pstRowInfo->ucRowIndex, pstRowInfo->usY,
                    pstRowInfo->ucRowLen, pstRowInfo->ucComponentCount,
                    pstRowInfo->pucColumnStart - pstIndex->pucIndexDDRAddr + OSD_INDEX_BUFFER_BASE_ADDR);

            Hv_Drv_OsdFont_DrawOneRow(pstRowInfo, HV_FALSE);
        }

#if (HV_OSD_CONFIG_RESERVE_SLOTS_BYROW == HV_CONFIG_OFF)
        pstPostBank = Hv_Drv_OsdResource_GetCurrentLoadedBank();
        pstCatalog->usTotalSlots = pstPostBank->usCurrentSlots;
        /* this catalog occupy two bank */
        if (pstPostBank != pstLoadedBank)
        {
            pstCatalog->usTotalSlots += OSD_BANK_CAPACITY;
        }

        OSD_DRV_LOG_DEBUG("catalog has %d total slots, post bank has %d",
                pstCatalog->usTotalSlots, pstPostBank->usCurrentSlots);

        /* allocate resource region */
        usBytes = Hv_Drv_OsdCommon_GetResourceBytes(pstCatalog->enSize, pstCatalog->enType);
        Hv_Drv_OsdResource_AllocRegion(pstPostBank->usCurrentSlots * usBytes);
#endif
    }

    pstIndex->usIndexSize = pucCurColumn - pstIndex->pucIndexDDRAddr;
    Hv_Drv_OsdIndex_AllocRegion(pstIndex->usIndexSize);
}

static VOID Hv_Drv_OsdFont_SetBlankRowFontIndex(OsdPhyFontInfo *pstFontInfo)
{
    UCHAR8 ucRowIndex = 0;
    OsdIndexRow *pstRow = NULL;
    OsdIndexInfo *pstIndex = &pstFontInfo->stIndexInfo;

    for (ucRowIndex = 0; ucRowIndex < pstIndex->ucRowCount - 1; ++ucRowIndex)
    {
        pstRow = Hv_Drv_OsdIndex_GetRow(ucRowIndex);
        if (Hv_Cal_OsdIndex_GetRowEnable(pstRow))
        {
            continue;
        }

        Hv_Cal_OsdIndex_SetRowFontLibIndex(pstRow, Hv_Drv_OsdResource_SearchForBlank());
    }
}

/**
 * Font Redraw
 */
Status Hv_Drv_OsdFont_DrawMenuFontList(OsdWindow *pstWindow)
{
    OsdNodeHead *pstComponentsHead = &pstWindow->stFontShowListHead;
    OsdPhyFontInfo *pstFontInfo = NULL;
    OsdIndexInfo *pstIndex = NULL;
    UCHAR8 ucWinIndex = 0;
    Status uiStatus = HV_SUCCESS;

    OSD_DRIVER_MEMORY_PRESERVE();

    ucWinIndex = pstWindow->ucPhyId;
    if (ucWinIndex > PHY_WIN_3)
    {
        OSD_DRV_LOG_DEBUG("Not font window");
        goto out;
    }

    Hv_Drv_OsdWindow_SetCurrentWindow(ucWinIndex);
    if (NULL == pstComponentsHead->pstNode)
    {
        OSD_DRV_LOG_DEBUG("no fonts drawn");
        Hv_Drv_OsdFont_SetFontEnable(0);

        goto out;
    }
    OSD_DRV_LOG_DEBUG("Osd Begin to draw fonts of window %u", ucWinIndex);

    pstFontInfo = Hv_Drv_OsdFont_GetCurrentFontInfo();
    pstIndex = &pstFontInfo->stIndexInfo;
    Hv_Drv_OsdFont_PrepareRedrawing(pstWindow, pstFontInfo);

    /* sort components into normal rows */
    uiStatus = Hv_Drv_OsdFont_SortComponents(pstFontInfo, pstComponentsHead);
    if (HV_FAILURE == uiStatus)
    {
        goto out;
    }

    /* allocate index rows */
    Hv_Drv_OsdFont_AllocIndexRows(pstFontInfo);

    /* actual drawing of components */
    Hv_Drv_OsdFont_FillIndexColumns(pstFontInfo);

    /* set font index for blank rows */
    Hv_Drv_OsdFont_SetBlankRowFontIndex(pstFontInfo);

    /* 设置window中index相关寄存器*/
    Hv_Cal_OsdWindow_SetRowIndexAddr((USHORT16)((UINT32)pstIndex->pucIndexDDRAddr - OSD_INDEX_DDR_START));
    Hv_Cal_OsdWindow_SetRowCount(pstIndex->ucRowCount);

    Hv_Drv_OsdFont_SetResource();

    Hv_Drv_OsdIndex_SetChangeRange(OSD_SRAM_CHANGE_FULL, 0, 0);
    Hv_Drv_OsdResource_SetChangeRange(OSD_SRAM_CHANGE_FULL, 0, 0);

    /* font maybe disabled when rotate */
    Hv_Drv_OsdFont_SetFontEnable(1);

    OSD_DRV_LOG_DEBUG("Draw fonts of window %d end", ucWinIndex);

out:
    OSD_DRIVER_MEMORY_RESTORE();

    return uiStatus;
}

#if (HV_OSD_CONFIG_RESERVE_SLOTS_BYROW == HV_CONFIG_OFF)
static OsdRadixTree* Hv_Drv_OsdFont_CreateSlotsManger(OsdRowCatalog *pstCatalog)
{
    USHORT16 usTotalSlots = 0;
    UCHAR8 ucSlotIndex = 0;
    OsdRadixTree *pstTree = NULL;

    HV_ASSERT_TRUE_RET((pstCatalog != NULL) && (pstCatalog->pstSlotsManager == NULL), NULL);

    usTotalSlots = pstCatalog->usTotalSlots;
    pstTree = Hv_Drv_OsdRadix_BuildTree(usTotalSlots);
    if (NULL == pstTree)
    {
        OSD_DRV_LOG_ERROR("osd fatal error, not enough space to create slots manager");
        return NULL;
    }

    Hv_Drv_OsdRadix_MarkSlots(pstTree, usTotalSlots);

    /* slots maybe unused */
    if (0 != pstCatalog->ucUnusedSlots)
    {
        OSD_DRV_LOG_DEBUG("Create slots manager for catalog(%s %s), "
                "drawn resource count: %d, unused resource count: %d",
                Hv_Drv_OsdCommon_GetResourceSizeStr(pstCatalog->enSize),
                Hv_Drv_OsdCommon_GetResourceTypeStr(pstCatalog->enType),
                usTotalSlots - pstCatalog->ucUnusedSlots,
                pstCatalog->ucUnusedSlots);

        /* free unused slots */
        for (ucSlotIndex = 0; ucSlotIndex < pstCatalog->ucUnusedSlots; ++ucSlotIndex)
        {
            Hv_Drv_OsdRadix_FreeSlot(pstTree, OSD_BANK_CAPACITY - 1 - ucSlotIndex);
        }
    }
    else
    {
        OSD_DRV_LOG_DEBUG("Create slots manager for catalog(%s %s), "
                "drawn resource count: %d, no unused slots",
                Hv_Drv_OsdCommon_GetResourceSizeStr(pstCatalog->enSize),
                Hv_Drv_OsdCommon_GetResourceTypeStr(pstCatalog->enType),
                usTotalSlots);
    }

    pstCatalog->pstSlotsManager = pstTree;
    return pstTree;
}

VOID Hv_Drv_OsdFont_FreeRangedRowSlots(OsdRowInfo *pstRowInfo, OsdIndexColumn *pstColumnStart, OsdIndexColumn *pstColumnEnd, BOOL bSkipCPUCanvas)
{
    OsdRowCatalog *pstCatalog = pstRowInfo->pstCatalog;
    OsdUpdateManager *pstManager = pstCatalog->pstUpdateManager;
    OsdRadixTree *pstTree = NULL;
    OsdIndexColumn *pstColumn = NULL;
    USHORT16 usSlot = 0;
    UCHAR8 ucIndex = 0;
    BOOL bLeftTree = HV_TRUE;

    OSD_DRV_LOG_DEBUG("free slots for row %d, range: 0x%x - 0x%x", pstRowInfo->usY, pstColumnStart, pstColumnEnd);

    if (pstManager && (pstManager->pstUpdateBank == pstRowInfo->pstLoadedBank))
    {
        OSD_DRV_LOG_DEBUG("free row slots from update tree");
        pstTree = pstManager->pstUpdateSlotsManager;
    }
    else
    {
        /* which half of tree */
        if (pstRowInfo->pstLoadedBank != pstCatalog->pstCatalogBank)
        {
            bLeftTree = HV_FALSE;

            OSD_DRV_LOG_DEBUG("free row slots from redraw-right tree");
        }
        else
        {
            OSD_DRV_LOG_DEBUG("free row slots from redraw-left tree");
        }
        pstTree = pstCatalog->pstSlotsManager;
    }

    pstColumn = (OsdIndexColumn*)pstRowInfo->pucColumnStart;
    HV_ASSERT_TRUE_VOID(pstColumnStart >= pstColumn);
    HV_ASSERT_TRUE_VOID(pstColumnEnd >= pstColumnStart);
    HV_ASSERT_TRUE_VOID(pstColumnEnd < (pstColumn + pstRowInfo->ucRowLen));

    for (; pstColumnStart <= pstColumnEnd; ++pstColumnStart)
    {
#if (HV_PROJECT_CONFIG_OSD_CPU_CANVAS_SUPPORT == HV_CONFIG_ON)
        if (bSkipCPUCanvas && (0 != pstRowInfo->ucCPUCanvasColumnCount))
        {
            if ((pstColumnStart >= pstRowInfo->pstCPUCanvasStartColumn) &&
                (pstColumnStart < pstRowInfo->pstCPUCanvasStartColumn + pstRowInfo->ucCPUCanvasColumnCount))
            {
                /* skip CPU Canvas */
                continue;
            }
        }
#endif
        if (Hv_Cal_OsdIndex_IsBlankColumn(pstColumnStart))
        {
            continue;
        }

        usSlot = Hv_Cal_OsdIndex_GetColIndex(pstColumnStart);
        if (!bLeftTree)
        {
            usSlot += OSD_BANK_CAPACITY;
        }
        Hv_Drv_OsdRadix_FreeSlot(pstTree, usSlot);

        Hv_Cal_OsdIndex_SetColType(pstColumnStart, OSD_COLUMN_BLANK);
        Hv_Cal_OsdIndex_SetColBlankWidth(pstColumnStart, pstRowInfo->ucBlankWidth);
    }

    return;
}

VOID Hv_Drv_OsdFont_FreeRowSlots(OsdRowInfo *pstRowInfo)
{
    OsdIndexColumn *pstColumn = NULL;

    pstColumn = (OsdIndexColumn*)pstRowInfo->pucColumnStart;
    Hv_Drv_OsdFont_FreeRangedRowSlots(pstRowInfo, pstColumn, pstColumn + pstRowInfo->ucRowLen - 1, HV_TRUE);

    return;
}
#endif

static VOID Hv_Drv_OsdFont_UpdateRecordRow(OsdRowInfo *pstRowInfo, OsdRowInfo **ppUpdatedRows, UCHAR8 *pucCount)
{
    UCHAR8 ucIndex = 0;
    BOOL bFound = HV_FALSE;

    for (ucIndex = 0; ucIndex < *pucCount; ++ucIndex)
    {
        if (pstRowInfo == ppUpdatedRows[ucIndex])
        {
            bFound = HV_TRUE;
            break;
        }
    }
    if (!bFound)
    {
        ppUpdatedRows[(*pucCount)++] = pstRowInfo;
    }

    return;
}

static VOID Hv_Drv_OsdFont_UpdateRecordComponent(OsdComponentNode *pstCur, OsdRowInfo *pstRowInfo,
                                            OsdRowInfo **ppUpdatedRows, UCHAR8 *pucCount)
{
    struct LinkedList *pstNext = NULL;
    UCHAR8 ucRows = 0;
    UCHAR8 ucRowIndex = 0;
#if (HV_PROJECT_CONFIG_OSD_TEXT_WRAPPING == HV_CONFIG_ON)
    UCHAR8 ucHeight = 0;
    UCHAR8 ucWrapSpace = 0;
    USHORT16 usY = pstRowInfo->usY;
    USHORT16 usCurY = 0;
    BOOL bConsistent = HV_FALSE;
    OsdWrapNode *pstWrap = NULL;

    if (Hv_Drv_OsdFont_IsWrapText(pstCur))
    {
        pstWrap = Hv_Drv_OsdFont_FindWrapNode(pstCur, HV_FALSE);
        if (NULL == pstWrap)
        {
            OSD_DRV_LOG_ERROR("Could not record non-exist wrap component %s", CP_NAME(pstCur->stCp));

            return;
        }

        if (pstWrap->apDrawnRows[0] == pstRowInfo)
        {
            bConsistent = HV_TRUE;
        }
    }

    ucWrapSpace = Hv_Drv_OsdFont_GetWrapSpace(pstCur);

    Hv_Drv_OsdResource_GetSize(pstRowInfo->enSize, NULL, &ucHeight);
#endif

    ucRows = Hv_Drv_OsdFont_GetComponentRows(pstCur);

    for (ucRowIndex = 0; ucRowIndex < ucRows; ++ucRowIndex)
    {
        Hv_Drv_OsdFont_UpdateRecordRow(pstRowInfo, ppUpdatedRows, pucCount);

#if (HV_PROJECT_CONFIG_OSD_TEXT_WRAPPING == HV_CONFIG_ON)
        if (Hv_Drv_OsdFont_IsWrapText(pstCur))
        {
            if (bConsistent)
            {
                pstRowInfo = pstWrap->apDrawnRows[ucRowIndex + 1];
            }
            else
            {
                usCurY = usY + (ucRowIndex + 1) * (ucHeight + ucWrapSpace);
                pstRowInfo = Hv_Drv_OsdFont_GetRowInfoByYaxis(usCurY, HV_FALSE);
                if (NULL == pstRowInfo)
                {
                    OSD_DRV_LOG_ERROR("Could not record %d-th row of component %s, row %d does not exist",
                            ucRowIndex + 1, CP_NAME(pstCur->stCp), usCurY);

                    return;
                }
            }
        }
        else
        {
#endif
            pstNext = pstRowInfo->stEntry.next;
            pstRowInfo = Hv_Comm_LinkedList_Entry(pstNext, OsdRowInfo, stEntry);
#if (HV_PROJECT_CONFIG_OSD_TEXT_WRAPPING == HV_CONFIG_ON)
        }
#endif
    }
}

#if (HV_PROJECT_CONFIG_OSD_TEXT_WRAPPING == HV_CONFIG_ON)
static OsdWrapDrawnInfo* Hv_Drv_OsdFont_AllocWrapDrawInfo(UCHAR8 ucMaxRows)
{
    USHORT16 usAllocSize = 0;
    OsdWrapDrawnInfo *pstWrapInfo = NULL;

    usAllocSize = sizeof(*pstWrapInfo) + ucMaxRows * sizeof(OsdRowInfo*);
    pstWrapInfo = HV_MALLOC(usAllocSize);
    if (NULL == pstWrapInfo)
    {
        OSD_DRV_LOG_ERROR("Failed to malloc wrap info: %d, size: %d", ucMaxRows, usAllocSize);

        return NULL;
    }
    HV_MEMSET(pstWrapInfo, 0, usAllocSize);

    Hv_Comm_LinkedList_Init(&pstWrapInfo->stEntry);

    return pstWrapInfo;
}

VOID Hv_Drv_OsdFont_UpdateRowsCheckWrap(OsdRowInfo **ppUpdatedRows, UCHAR8 *pucUpdatedRowCount)
{
    UCHAR8 ucIndex = 0;
    UCHAR8 ucRowIndex = 0;
    UCHAR8 ucPreIndex = 0;
    UCHAR8 ucMaxRows = 0;
    UCHAR8 ucHeight = 0;
    UCHAR8 ucUpdatedRowCountBackup = 0;
    USHORT16 usWrapSpace = 0;
    USHORT16 usY = 0;
    USHORT16 usCurY = 0;
    USHORT16 usAllocSize = 0;
    struct LinkedList *pstNode = NULL;
    struct LinkedList *pstNext = NULL;
    OsdComponentNode *pstComponent = NULL;
    OsdRowInfo *pstRowInfo = NULL;
    OsdRowInfo **apWrapRows = NULL;
    OsdWrapNode *pstWrap = NULL;
    struct LinkedList stRemainingWraps;
    OsdWrapDrawnInfo *pstWrapInfo = NULL;
    BOOL bFound = HV_FALSE;
    BOOL bAppend = HV_FALSE;

    Hv_Comm_LinkedList_Init(&stRemainingWraps);

    /* 1. individually collect all rows drawn by wrap texts */
    Hv_Comm_LinkedList_ForEach(&s_pstFontParam->stAllWrapNodes, pstNode)
    {
        pstWrap = Hv_Comm_LinkedList_Entry(pstNode, OsdWrapNode, stEntry);
        pstComponent = pstWrap->stIterator.pstComponent;

        usWrapSpace = Hv_Drv_OsdFont_GetWrapSpace(pstComponent);
        usY = Hv_Drv_OsdFont_GetComponentY(pstComponent);
        ucHeight = Hv_Drv_OsdFont_GetComponentResH(pstComponent);

        ucMaxRows = Hv_Drv_OsdFont_GetComponentRows(pstComponent);

        pstWrapInfo = Hv_Drv_OsdFont_AllocWrapDrawInfo(ucMaxRows);
        if (NULL == pstWrapInfo)
        {
            OSD_DRV_LOG_ERROR("update check wrap for component %s failed, not enough memory", CP_NAME(pstComponent->stCp));

            goto out_free;
        }

        pstWrapInfo->pstWrap = pstWrap;

        Hv_Comm_LinkedList_AddTail(&stRemainingWraps, &pstWrapInfo->stEntry);

        for (ucIndex = 0; ucIndex < ucMaxRows; ++ucIndex)
        {
            usCurY = usY + ucIndex * (usWrapSpace + ucHeight);
            /* could not use pstWrap->apDrawnRows, text may change position while upating */
            pstRowInfo = Hv_Drv_OsdFont_GetRowInfoByYaxis(usCurY, HV_FALSE);
            if (NULL == pstRowInfo)
            {
                OSD_DRV_LOG_ERROR("Could not find %d-th row of component %s: %d", ucIndex, CP_NAME(pstComponent->stCp), usCurY);
                break;
            }

            pstWrapInfo->ppRowInfos[ucIndex] = pstRowInfo;
        }
    }

    /* 2. check intersection of update array and wrap infos */
again:
    Hv_Comm_LinkedList_ForEachSafe(&stRemainingWraps, pstNext, pstNode)
    {
        pstWrapInfo = Hv_Comm_LinkedList_Entry(pstNode, OsdWrapDrawnInfo, stEntry);

        ucUpdatedRowCountBackup = *pucUpdatedRowCount;
        bAppend = HV_FALSE;

        for (ucRowIndex = 0; ucRowIndex < pstWrapInfo->ucRows; ++ucRowIndex)
        {
            bFound = HV_FALSE;
            for (ucIndex = 0; ucIndex < ucUpdatedRowCountBackup; ++ucIndex)
            {
                if (pstWrapInfo->ppRowInfos[ucRowIndex] == ppUpdatedRows[ucIndex])
                {
                    bFound = HV_TRUE;
                    /* already in update array, no need to append */
                    if (bAppend)
                    {
                        break;
                    }

                    /* need to merge update array and this wrap drawn rows */
                    bAppend = HV_TRUE;

                    /* merge previous rows into update array */
                    for (ucPreIndex = 0; ucPreIndex < ucRowIndex; ++ucPreIndex)
                    {
                        ppUpdatedRows[(*pucUpdatedRowCount)++] = pstWrapInfo->ppRowInfos[ucPreIndex];
                    }

                    break;
                }
            }

            /* row not found in update array, append */
            if ((bAppend) && (!bFound))
            {
                ppUpdatedRows[(*pucUpdatedRowCount)++] = pstWrapInfo->ppRowInfos[ucRowIndex];
            }
        }

        /* appended, wrap text would be redrawed later */
        if (bAppend)
        {
            pstWrap = pstWrapInfo->pstWrap;
            /* set curRow to 0 */
            pstWrap->ucCurRow = 0;
            /* reset iterator */
            Hv_Drv_OsdFont_IterReset(&pstWrap->stIterator);

            /* remove wrap from list, then it would not be checked again */
            Hv_Comm_LinkedList_Del(&stRemainingWraps, pstNode);

            /* free wrap info */
            HV_FREE(pstWrapInfo);

            /* different WrapText may cross each other, we need to check from the beginning */
            goto again;
        }
    }

    /* remaining wraps would not be updated */

out_free:
    /* free wrap drawn info */
    Hv_Comm_LinkedList_ForEachSafe(&stRemainingWraps, pstNext, pstNode)
    {
        Hv_Comm_LinkedList_Del(&stRemainingWraps, pstNode);

        pstWrapInfo = Hv_Comm_LinkedList_Entry(pstNode, OsdWrapDrawnInfo, stEntry);
        HV_FREE(pstWrapInfo);
    }

    return;
}
#endif

static INT32 rowSortByY(const VOID *pvRowA, const VOID *pvRowB)
{
    const OsdRowInfo *pstRowInfoA = (OsdRowInfo*)pvRowA;
    const OsdRowInfo *pstRowInfoB = (OsdRowInfo*)pvRowB;

    return (INT32)pstRowInfoA->usY - (INT32)pstRowInfoB->usY;
}

static Status Hv_Drv_OsdFont_UpdatePrepareComponent(OsdComponentNode *pstCur, OsdRowInfo **ppUpdatedRows, UCHAR8 *pucCount, BOOL bCalculateBox)
{
    UCHAR8 ucIndex = 0;
    UCHAR8 ucHeight = 0;
    USHORT16 usY = 0;
    OsdRowInfo *pstRowInfo = NULL;
    const OsdBlock *pstBlock = NULL;
    BOOL bFound = HV_FALSE;

    Hv_Drv_OsdFont_LogComponent(pstCur);

    pstBlock = Hv_Drv_OsdFont_GetBlock(pstCur);
    Hv_Drv_OsdResource_GetSize(pstBlock->usResourceSize, NULL, &ucHeight);

    usY = Hv_Drv_OsdFont_GetComponentY(pstCur);

#ifdef HV_SCALER_DEBUG_VERSION
    Hv_Drv_OsdDebug_SubComponentWidth(pstCur);
#endif

    if (NULL != pstCur->pstRowInfo)
    {
        pstRowInfo = pstCur->pstRowInfo;

        bFound = HV_FALSE;
        for (ucIndex = 0; ucIndex < pstRowInfo->ucComponentCount; ++ucIndex)
        {
            if (pstRowInfo->apCurrentComponents[ucIndex] == pstCur)
            {
                bFound = HV_TRUE;
                break;
            }
        }
        if (!bFound)
        {
            OSD_DRV_LOG_ERROR("Osd fatal error: updated CP-%s not found in rows", CP_NAME(pstCur->stCp));

            return HV_FAILURE;
        }

        /* reinsert */
        Hv_Drv_OsdFont_RowInfoRemoveComponent(pstRowInfo, pstCur, HV_TRUE);

        /* update may change component's row */
        if (pstRowInfo->usY != usY)
        {
            OSD_DRV_LOG_DEBUG("updated CP-%s changes row from %d to %d", CP_NAME(pstCur->stCp),
                    pstRowInfo->usY, usY);

            Hv_Drv_OsdFont_UpdateRecordComponent(pstCur, pstRowInfo, ppUpdatedRows, pucCount);

            pstRowInfo = Hv_Drv_OsdFont_GetRowInfoByYaxis(usY, HV_FALSE);
            if (NULL == pstRowInfo)
            {
                OSD_DRV_LOG_ERROR("Osd fatal error: update CP-%s need to insert row", CP_NAME(pstCur->stCp));

                return HV_FAILURE;
            }
        }
    }
    else
    {
        pstRowInfo = Hv_Drv_OsdFont_GetRowInfoByYaxis(usY, HV_FALSE);
        if (NULL == pstRowInfo)
        {
            OSD_DRV_LOG_ERROR("Osd fatal error: update CP-%s need to insert row", CP_NAME(pstCur->stCp));

            return HV_FAILURE;
        }
    }

    /* in case of component change position */
    if (bCalculateBox)
    {
        Hv_Drv_OsdFont_CalculateComponentBox(pstCur, HV_FALSE);
    }
    Hv_Drv_OsdFont_RowInfoInsertComponent(pstRowInfo, pstCur, HV_TRUE);

    Hv_Drv_OsdFont_UpdateRecordComponent(pstCur, pstRowInfo, ppUpdatedRows, pucCount);

    return HV_SUCCESS;
}

VOID Hv_Drv_OsdFont_UpdateMenuFontList(OsdWindow *pstWindow)
{
    UCHAR8 ucIndex = 0;
    UCHAR8 ucRows = 0;
    UCHAR8 ucCurRow = 0;
    USHORT16 usY = 0;
    OsdNodeHead *pstComponentsHead = &pstWindow->stFontShowListHead;
    USHORT16 usComponentCount = 0;
    OsdComponentNode *pstCur = NULL;
    struct LinkedList *pstNext = NULL;
    OsdPhyFontInfo *pstFontInfo = NULL;
    OsdIndexInfo *pstIndex = NULL;
    OsdRowInfo *pstRowInfo = NULL;
    OsdRowCatalog *pstCatalog = NULL;
    OsdRowInfo *apUpdatedRows[32];
    UCHAR8 ucUpdatedRowCount = 0;
    BOOL bFound = HV_FALSE;
    USHORT16 usBytes = 0;
    UCHAR8 ucHeight = 0;
    UCHAR8 ucWinIndex = 0;
    USHORT16 usSlots = 0;

    ucWinIndex = pstWindow->ucPhyId;
    Hv_Drv_OsdWindow_SetCurrentWindow(ucWinIndex);
    if (ucWinIndex > PHY_WIN_3)
    {
        OSD_DRV_LOG_DEBUG("Not font window");
        return;
    }
    OSD_DRV_LOG_DEBUG("Osd Begin to update fonts of window %u", ucWinIndex);
    for (ucIndex = 0; ucIndex < 32; ++ucIndex)
    {
        apUpdatedRows[ucIndex] = NULL;
    }

    pstFontInfo = Hv_Drv_OsdFont_GetCurrentFontInfo();
    pstIndex = &pstFontInfo->stIndexInfo;

    /* record all rows need to redraw */
    pstCur = pstComponentsHead->pstNode;
    while (usComponentCount++ < pstComponentsHead->usUpdateNum)
    {
        if (HV_FAILURE == Hv_Drv_OsdFont_UpdatePrepareComponent(pstCur, apUpdatedRows, &ucUpdatedRowCount, HV_TRUE))
        {
            return;
        }

        pstCur = pstCur->pstNext;
    }

    OSD_DRV_LOG_DEBUG("%d rows need to update", ucUpdatedRowCount);
    if (0 == ucUpdatedRowCount)
    {
        return;
    }

#if (HV_PROJECT_CONFIG_OSD_TEXT_WRAPPING == HV_CONFIG_ON)
    Hv_Drv_OsdFont_UpdateRowsCheckWrap(apUpdatedRows, &ucUpdatedRowCount);

    OSD_DRV_LOG_DEBUG("%d rows need to update after wrap check", ucUpdatedRowCount);

    /* wrap text requires ordered row drawing */
    qsort(apUpdatedRows, ucUpdatedRowCount, sizeof(OsdRowInfo*), rowSortByY);
#endif

#if (HV_OSD_CONFIG_RESERVE_SLOTS_BYROW == HV_CONFIG_OFF)
    /* free slots occupied by one row */
    for (ucIndex = 0; ucIndex < ucUpdatedRowCount; ++ucIndex)
    {
        pstRowInfo = apUpdatedRows[ucIndex];
        pstCatalog = pstRowInfo->pstCatalog;

        /* check if slots manager created? */
        if (NULL == pstCatalog->pstSlotsManager)
        {
            if (NULL == Hv_Drv_OsdFont_CreateSlotsManger(pstCatalog))
            {
                return;
            }
        }

        Hv_Drv_OsdFont_FreeRowSlots(pstRowInfo);

#if (HV_PROJECT_CONFIG_OSD_MARQUEE == HV_CONFIG_ON)
        Hv_Drv_OsdFont_MarqueeMarkSlotsFreedByRow(pstRowInfo);
#endif
    }
#endif

    for (ucIndex = 0; ucIndex < ucUpdatedRowCount; ++ucIndex)
    {
        pstRowInfo = apUpdatedRows[ucIndex];

#if (HV_OSD_CONFIG_RESERVE_SLOTS_BYROW == HV_CONFIG_OFF)
        Hv_Drv_OsdFont_SetCurrentCatalog(pstRowInfo->pstCatalog);
#endif

#if (HV_PROJECT_CONFIG_OSD_CPU_CANVAS_SUPPORT == HV_CONFIG_ON)
        /* copy out CPU Canvas area */
        if (0 != pstRowInfo->ucCPUCanvasColumnCount)
        {
            HV_MEMCPY(s_pstFontParam->aucCPUCanvasColumns, pstRowInfo->pstCPUCanvasStartColumn,
                    pstRowInfo->ucCPUCanvasColumnCount * OSD_INDEX_COLUMN_SIZE);
        }
#endif
        Hv_Drv_OsdFont_DrawOneRow(pstRowInfo, HV_TRUE);

#if (HV_OSD_CONFIG_RESERVE_SLOTS_BYROW == HV_CONFIG_ON)
        Hv_Drv_OsdIndex_SetChangeRange(OSD_SRAM_CHANGE_PARTIAL,
                (UINT32)pstRowInfo->pucColumnStart,
                (UINT32)(pstRowInfo->pucColumnStart
                    + pstRowInfo->ucRowLen * OSD_INDEX_COLUMN_SIZE));

        usBytes = Hv_Drv_OsdCommon_GetResourceBytes(pstRowInfo->enSize, pstRowInfo->enType);

        Hv_Drv_OsdResource_SetChangeRange(OSD_SRAM_CHANGE_PARTIAL,
                pstRowInfo->usResourceOffset,
                pstRowInfo->usResourceOffset + pstRowInfo->ucCurIndex * usBytes);
#endif
    }

#if (HV_OSD_CONFIG_RESERVE_SLOTS_BYROW == HV_CONFIG_OFF)
    Hv_Drv_OsdIndex_SetChangeRange(OSD_SRAM_CHANGE_FULL, 0, 0);
    Hv_Drv_OsdResource_SetChangeRange(OSD_SRAM_CHANGE_FULL, 0, 0);
#endif

    OSD_DRV_LOG_DEBUG("osd update done!");
}

static VOID Hv_Drv_OsdFont_HidePrepareComponent(OsdComponentNode *pstCur, OsdRowInfo **ppUpdatedRows, UCHAR8 *pucCount)
{
    UCHAR8 ucRows = 0;
    UCHAR8 ucCurRow = 0;
    USHORT16 usY = 0;
    struct LinkedList *pstNext = NULL;
    OsdRowInfo *pstRowInfo = NULL;
    const OsdBlock *pstBlock = NULL;
    UCHAR8 ucIndex = 0;
    BOOL bFound = HV_FALSE;
    UCHAR8 ucHeight = 0;
#if (HV_PROJECT_CONFIG_OSD_TEXT_WRAPPING == HV_CONFIG_ON)
    OsdWrapNode *pstWrap = NULL;
#endif
#if (HV_PROJECT_CONFIG_OSD_CPU_CANVAS_SUPPORT == HV_CONFIG_ON)
    OsdIndexColumn *pstColumn = NULL;
#endif

    Hv_Drv_OsdFont_LogComponent(pstCur);

#if (HV_PROJECT_CONFIG_OSD_MARQUEE == HV_CONFIG_ON)
    /* remove marquee node */
    Hv_Drv_OsdFont_RemoveMarqueeNode(pstCur->pstMarquee);
#endif

#if (HV_PROJECT_CONFIG_OSD_TEXT_WRAPPING == HV_CONFIG_ON)
    pstWrap = NULL;
    if (Hv_Drv_OsdFont_IsWrapText(pstCur))
    {
        pstWrap = Hv_Drv_OsdFont_FindWrapNode(pstCur, HV_FALSE);
        if (NULL == pstWrap)
        {
            OSD_DRV_LOG_ERROR("Could not remove non-exist wrap component %s", CP_NAME(pstCur->stCp));

            continue;
        }
    }
#endif
    pstBlock = Hv_Drv_OsdFont_GetBlock(pstCur);
    Hv_Drv_OsdResource_GetSize(pstBlock->usResourceSize, NULL, &ucHeight);

    usY = Hv_Drv_OsdFont_GetComponentY(pstCur);

    if (NULL == pstCur->pstRowInfo)
    {
        OSD_DRV_LOG_DEBUG("try to hide a not drawn CP-%s", CP_NAME(pstCur->stCp));

        //pstCur = pstCur->pstNext;
        return;
    }

    pstRowInfo = pstCur->pstRowInfo;

    bFound = HV_FALSE;
    for (ucIndex = 0; ucIndex < pstRowInfo->ucComponentCount; ++ucIndex)
    {
        if (pstRowInfo->apCurrentComponents[ucIndex] == pstCur)
        {
            OSD_DRV_LOG_DEBUG("found component at index: %d", ucIndex);
            bFound = HV_TRUE;
            break;
        }
    }

    if (!bFound)
    {
        OSD_DRV_LOG_ERROR("Osd fatal error: hiding CP-%s not found in rows", CP_NAME(pstCur->stCp));

        //pstCur = pstCur->pstNext;
        return;
    }

#ifdef HV_SCALER_DEBUG_VERSION
    Hv_Drv_OsdDebug_SubComponentWidth(pstCur);
#endif

    ucRows = Hv_Drv_OsdFont_GetComponentRows(pstCur);

    for (ucCurRow = 0; ucCurRow < ucRows; ++ucCurRow)
    {
#if (HV_PROJECT_CONFIG_OSD_CPU_CANVAS_SUPPORT == HV_CONFIG_ON)
        /* free slots here */
        if (Hv_Drv_OsdFont_IsCPUCanvas(pstCur))
        {
            pstColumn = pstRowInfo->pstCPUCanvasStartColumn;
            Hv_Drv_OsdFont_FreeRangedRowSlots(pstRowInfo, pstColumn, pstColumn + pstRowInfo->ucCPUCanvasColumnCount - 1, HV_FALSE);

            pstRowInfo->pstCPUCanvasStartColumn = NULL;
            pstRowInfo->ucCPUCanvasColumnCount = 0;
        }
#endif
        Hv_Drv_OsdFont_RowInfoRemoveComponent(pstRowInfo, pstCur, HV_FALSE);

        Hv_Drv_OsdFont_UpdateRecordRow(pstRowInfo, ppUpdatedRows, pucCount);

        usY += ucHeight;

#if (HV_PROJECT_CONFIG_OSD_TEXT_WRAPPING == HV_CONFIG_ON)
        if (NULL != pstWrap)
        {
            pstRowInfo = pstWrap->apDrawnRows[ucCurRow + 1];
        }
        else
        {
#endif
            pstNext = pstRowInfo->stEntry.next;
            pstRowInfo = Hv_Comm_LinkedList_Entry(pstNext, OsdRowInfo, stEntry);
#if (HV_PROJECT_CONFIG_OSD_TEXT_WRAPPING == HV_CONFIG_ON)
        }
#endif
    }

    /* remove wrap nodes */
#if (HV_PROJECT_CONFIG_OSD_TEXT_WRAPPING == HV_CONFIG_ON)
    if (NULL != pstWrap)
    {
        Hv_Drv_OsdFont_RemoveWrapNode(pstWrap);
    }
#endif
}

VOID Hv_Drv_OsdFont_HideMenuFontList(OsdWindow *pstWindow)
{
    UCHAR8 ucRows = 0;
    UCHAR8 ucCurRow = 0;
    USHORT16 usY = 0;
    OsdNodeHead *pstComponentsHead = &pstWindow->stFontDeleteListHead;
    OsdComponentNode *pstCur = NULL;
    struct LinkedList *pstNext = NULL;
    OsdPhyFontInfo *pstFontInfo = NULL;
    OsdIndexInfo *pstIndex = NULL;
    OsdRowInfo *pstRowInfo = NULL;
    OsdRowCatalog *pstCatalog = NULL;
    const OsdBlock *pstBlock = NULL;
    UCHAR8 ucIndex = 0;
    OsdRowInfo *apUpdatedRows[32];
    UCHAR8 ucUpdatedRowCount = 0;
    BOOL bFound = HV_FALSE;
    USHORT16 usBytes = 0;
    UCHAR8 ucHeight = 0;
    UCHAR8 ucWinIndex = 0;
#if (HV_PROJECT_CONFIG_OSD_TEXT_WRAPPING == HV_CONFIG_ON)
    OsdWrapNode *pstWrap = NULL;
#endif

    ucWinIndex = pstWindow->ucPhyId;
    Hv_Drv_OsdWindow_SetCurrentWindow(ucWinIndex);
    if (ucWinIndex > PHY_WIN_3)
    {
        OSD_DRV_LOG_DEBUG("Not font window");
        return;
    }

    OSD_DRV_LOG_DEBUG("Osd Begin to hide fonts of window %u", ucWinIndex);

    for (ucIndex = 0; ucIndex < 32; ++ucIndex)
    {
        apUpdatedRows[ucIndex] = NULL;
    }

    pstFontInfo = Hv_Drv_OsdFont_GetCurrentFontInfo();
    pstIndex = &pstFontInfo->stIndexInfo;

    /* record all rows need to redraw */
    pstCur = pstComponentsHead->pstNode;
    while (NULL != pstCur)
    {
        Hv_Drv_OsdFont_HidePrepareComponent(pstCur, apUpdatedRows, &ucUpdatedRowCount);

        pstCur = pstCur->pstNext;
    }

    OSD_DRV_LOG_DEBUG("%d rows need to update", ucUpdatedRowCount);
    if (0 == ucUpdatedRowCount)
    {
        return;
    }

#if (HV_PROJECT_CONFIG_OSD_TEXT_WRAPPING == HV_CONFIG_ON)
    Hv_Drv_OsdFont_UpdateRowsCheckWrap(apUpdatedRows, &ucUpdatedRowCount);

    OSD_DRV_LOG_DEBUG("%d rows need to update after wrap check", ucUpdatedRowCount);

    /* wrap text requires ordered row drawing */
    qsort(apUpdatedRows, ucUpdatedRowCount, sizeof(OsdRowInfo*), rowSortByY);
#endif

#if (HV_OSD_CONFIG_RESERVE_SLOTS_BYROW == HV_CONFIG_OFF)
    /* free slots occupied by one row */
    for (ucIndex = 0; ucIndex < ucUpdatedRowCount; ++ucIndex)
    {
        pstRowInfo = apUpdatedRows[ucIndex];
        pstCatalog = pstRowInfo->pstCatalog;

        /* check if slots manager created? */
        if (NULL == pstCatalog->pstSlotsManager)
        {
            if (NULL == Hv_Drv_OsdFont_CreateSlotsManger(pstCatalog))
            {
                return;
            }
        }

        Hv_Drv_OsdFont_FreeRowSlots(pstRowInfo);
#if (HV_PROJECT_CONFIG_OSD_MARQUEE == HV_CONFIG_ON)
        Hv_Drv_OsdFont_MarqueeMarkSlotsFreedByRow(pstRowInfo);
#endif
    }
#endif

    for (ucIndex = 0; ucIndex < ucUpdatedRowCount; ++ucIndex)
    {
        pstRowInfo = apUpdatedRows[ucIndex];

#if (HV_OSD_CONFIG_RESERVE_SLOTS_BYROW == HV_CONFIG_OFF)
        Hv_Drv_OsdFont_SetCurrentCatalog(pstRowInfo->pstCatalog);
#endif

        Hv_Drv_OsdFont_DrawOneRow(pstRowInfo, HV_TRUE);

#if (HV_OSD_CONFIG_RESERVE_SLOTS_BYROW == HV_CONFIG_ON)
        Hv_Drv_OsdIndex_SetChangeRange(OSD_SRAM_CHANGE_PARTIAL,
                (UINT32)pstRowInfo->pucColumnStart,
                (UINT32)(pstRowInfo->pucColumnStart
                    + pstRowInfo->ucRowLen * OSD_INDEX_COLUMN_SIZE));
        usBytes = Hv_Drv_OsdCommon_GetResourceBytes(pstRowInfo->enSize, pstRowInfo->enType);

        Hv_Drv_OsdResource_SetChangeRange(OSD_SRAM_CHANGE_PARTIAL,
                pstRowInfo->usResourceOffset,
                pstRowInfo->usResourceOffset + pstRowInfo->ucCurIndex * usBytes);
#endif
    }

#if (HV_OSD_CONFIG_RESERVE_SLOTS_BYROW == HV_CONFIG_OFF)
    Hv_Drv_OsdIndex_SetChangeRange(OSD_SRAM_CHANGE_FULL, 0, 0);
    Hv_Drv_OsdResource_SetChangeRange(OSD_SRAM_CHANGE_FULL, 0, 0);
#endif

    OSD_DRV_LOG_DEBUG("osd hide component done!");
}

VOID Hv_Drv_OsdFont_Init(VOID)
{
    UCHAR8 ucIndex = 0;
    OsdRowInfo *pstRowInfo = NULL;
    OsdRowInfo *pstCur = NULL;

    s_pstFontParam = (OsdFontParam*)OSD_DRIVER_ALLOC_STATIC(OsdFontParam, 1);
    HV_MEMSET(s_pstFontParam, 0, sizeof(*s_pstFontParam));
    Hv_Comm_LinkedList_Init(&s_pstFontParam->stRowInfoFreeList);
#if (HV_OSD_CONFIG_RESERVE_SLOTS_BYROW == HV_CONFIG_OFF)
    Hv_Comm_LinkedList_Init(&s_pstFontParam->stAllUpdateManagers);
#endif
#if (HV_PROJECT_CONFIG_OSD_MARQUEE == HV_CONFIG_ON)
    Hv_Comm_LinkedList_Init(&s_pstFontParam->stMarqueeHead);
#endif
#if (HV_PROJECT_CONFIG_OSD_TEXT_WRAPPING == HV_CONFIG_ON)
    Hv_Comm_LinkedList_Init(&s_pstFontParam->stAllWrapNodes);
#endif
#if (HV_PROJECT_CONFIG_OSD_CUSTOM_GROUP_SUPPORT == HV_CONFIG_ON)
    Hv_Comm_LinkedList_Init(&s_pstFontParam->stAllCustomGroups);
#endif

    s_pstFontParam->pstOsdPhyFonts = (OsdPhyFontInfo*)OSD_DRIVER_ALLOC_STATIC(OsdPhyFontInfo, OSD_FONT_WINDOW_NUMS);
    HV_MEMSET(s_pstFontParam->pstOsdPhyFonts, 0, OSD_FONT_WINDOW_NUMS * sizeof(*s_pstFontParam->pstOsdPhyFonts));
    OSD_DRV_LOG_DEBUG("PHY window font info allocted at 0x%x", s_pstFontParam->pstOsdPhyFonts);

    for (ucIndex = 0; ucIndex < OSD_FONT_WINDOW_NUMS; ++ucIndex)
    {
        s_pstFontParam->pstOsdPhyFonts[ucIndex].ucBlankBankIndex = 0xff;

        Hv_Comm_LinkedList_Init(&s_pstFontParam->pstOsdPhyFonts[ucIndex].stIndexInfo.stRowInfoList);
    }

    pstRowInfo = (OsdRowInfo*)OSD_DRIVER_ALLOC_STATIC(OsdRowInfo, OSD_MAX_ROW_COUNT);

    OSD_DRV_LOG_DEBUG("alloc %d rowInfo at 0x%x", OSD_MAX_ROW_COUNT, pstRowInfo);

    for (ucIndex = 0; ucIndex < OSD_MAX_ROW_COUNT; ++ucIndex)
    {
        pstCur = pstRowInfo + ucIndex;

        Hv_Comm_LinkedList_Init(&pstCur->stEntry);
        Hv_Comm_LinkedList_AddTail(&s_pstFontParam->stRowInfoFreeList, &pstCur->stEntry);
    }
    s_pstFontParam->ucRowInfoFreeCount = OSD_MAX_ROW_COUNT;
}

#ifdef HV_SCALER_DEBUG_VERSION

#if (HV_OSD_CONFIG_RESERVE_SLOTS_BYROW == HV_CONFIG_OFF)
VOID Hv_Drv_OsdFont_DebugUpdateManager(OsdUpdateManager *pstManager)
{
    OSD_LOG_INFO("UpdateManager resource offset %d size %d Bytes.",
            pstManager->usResourceOffset, pstManager->usSize);

    Hv_Drv_OsdRadix_PrintTree(pstManager->pstUpdateSlotsManager);
}

VOID Hv_Drv_OsdFont_DebugCatalogInfo(OsdRowCatalog *pstCatalog)
{
    OsdUpdateManager *pstManager = pstCatalog->pstUpdateManager;
    OsdRadixTree *pstTree = pstCatalog->pstSlotsManager;
    OsdPhyFontInfo *pstFontInfo = NULL;
    OsdIndexInfo *pstIndexInfo = NULL;
    OsdRowCatalogNode *pstCatalogNode = NULL;
    USHORT16 usBytes = 0;

    pstFontInfo = Hv_Drv_OsdFont_GetCurrentFontInfo();
    pstIndexInfo = &pstFontInfo->stIndexInfo;

    usBytes = Hv_Drv_OsdCommon_GetResourceBytes(pstCatalog->enSize, pstCatalog->enType);
    OSD_LOG_INFO("Catalog(%s %s) total slots %d resource offset %d, size %d Bytes",
            Hv_Drv_OsdCommon_GetResourceSizeStr(pstCatalog->enSize),
            Hv_Drv_OsdCommon_GetResourceTypeStr(pstCatalog->enType),
            pstCatalog->usTotalSlots, pstCatalog->usResourceOffset,
            pstCatalog->usTotalSlots * usBytes);

    if (NULL != pstTree)
    {
        OSD_LOG_INFO("slots manager created.");
        Hv_Drv_OsdRadix_PrintTree(pstTree);
    }
    else
    {
        OSD_LOG_INFO("slots manager not created.");
    }

    if (NULL != pstManager)
    {
        OSD_LOG_INFO("update manager created.");
        Hv_Drv_OsdFont_DebugUpdateManager(pstManager);
    }
    else
    {
        OSD_LOG_INFO("update manager not created.");
    }
}

VOID Hv_Drv_OsdFont_DebugWindowInfo(UCHAR8 ucIndex)
{
    OsdPhyFontInfo *pstFontInfo = NULL;
    OsdIndexInfo *pstIndexInfo = NULL;
    OsdRowCatalog *pstCatalog = NULL;
    struct LinkedList *pstNode = NULL;
    OsdRowInfo *pstRowInfo = NULL;
    UCHAR8 ucRowIndex = 0;
    UCHAR8 ucComponentIndex = 0;
    OsdComponentNode *pstComponent = NULL;
    OsdBox *pstBox = NULL;
    BOOL bEnable = HV_FALSE;

    OSD_LOG_INFO("================ window-%d debug start ================", ucIndex);

    Hv_Drv_OsdWindow_SetCurrentWindow(ucIndex);
    bEnable = Hv_Cal_OsdWindow_GetEnable();
    if (!bEnable)
    {
        OSD_LOG_INFO("window-%d is not enabled", ucIndex);
        return;
    }

    pstFontInfo = Hv_Drv_OsdFont_GetCurrentFontInfo();
    pstIndexInfo = &pstFontInfo->stIndexInfo;
    pstCatalog = pstFontInfo->pstCatalogs;

    while (NULL != pstCatalog)
    {
        Hv_Drv_OsdFont_DebugCatalogInfo(pstCatalog);

        pstCatalog = pstCatalog->pstNext;
    }

    Hv_Comm_LinkedList_ForEach(&pstIndexInfo->stRowInfoList, pstNode)
    {
        pstRowInfo = Hv_Comm_LinkedList_Entry(pstNode, OsdRowInfo, stEntry);

        OSD_LOG_INFO("Row %d Y %u columns %d component count %d column addr 0x%x",
                ucRowIndex++, pstRowInfo->usY, pstRowInfo->ucRowLen, pstRowInfo->ucComponentCount,
                pstRowInfo->pucColumnStart - pstIndexInfo->pucIndexDDRAddr + OSD_INDEX_BUFFER_BASE_ADDR);

        for (ucComponentIndex = 0; ucComponentIndex < pstRowInfo->ucComponentCount; ++ucComponentIndex)
        {
            pstComponent = pstRowInfo->apCurrentComponents[ucComponentIndex];
            pstBox = &pstComponent->stBox;

            OSD_LOG_INFO("        %d-th component(0x%x): %s box(%d, %d, %d, %d)", ucComponentIndex, pstComponent,
                    CP_NAME(pstComponent->stCp), pstBox->usX, pstBox->usY, pstBox->usW, pstBox->usH);
        }
    }

    OSD_LOG_INFO("window-%d draws %d rows", ucIndex, ucRowIndex);

    OSD_LOG_INFO("================ window-%d debug end ================\n", ucIndex);
}
#endif

VOID Hv_Drv_OsdFont_DubugInfo(VOID)
{
    UCHAR8 ucIndex = 0;

    //OSD_LOG_INFO("Font-CN Matrix total size = %d Bytes", g_usOsdFontMatrixCNLangLength);
    OSD_LOG_INFO("Font-Global Matrix total size = %d Bytes", g_usOsdFontMatrixGlobalLangLength);
    OSD_LOG_INFO("Icon Matrix total size = %d Bytes", g_usOsdIcon2MatrixLength);
    //OSD_LOG_INFO("Bmp Matrix total size = %d Bytes", g_usOsdBmpMatrixLength);
    OSD_LOG_INFO("Palette total size = %d Bytes", g_ausOsdPalettesListLength);
    OSD_LOG_INFO("current total component width: %u max width: %u", g_usTotalComponentWidth, g_usMaxComponentWidth);
    OSD_LOG_INFO("Index current total size = %d Bytes, max size = %d Bytes", (UINT32)Hv_Drv_OsdIndex_GetCurrentAddress() - OSD_INDEX_DDR_START, g_usMaxIndexSize);
    OSD_LOG_INFO("Resource real total size = %d Bytes, max size = %d Bytes", g_usResourceRealTotalSize, g_usResourceRealTotalSizeMax);
    OSD_LOG_INFO("Resource DMA transfer total size = %d Bytes", Hv_Drv_OsdResource_GetCurrentOffset());

#if (HV_OSD_CONFIG_RESERVE_SLOTS_BYROW == HV_CONFIG_OFF)
    for (ucIndex = 0; ucIndex < OSD_FONT_WINDOW_NUMS; ++ucIndex)
    {
        Hv_Drv_OsdFont_DebugWindowInfo(ucIndex);
    }
#endif
}

#if (HV_PROJECT_CONFIG_OSD_CPU_CANVAS_SUPPORT == HV_CONFIG_ON)
#ifdef CPUCANVAS_TEST
typedef struct _OsdPoint
{
    USHORT16 usY;
    struct LinkedList stEntry;
} OsdPoint;
static VOID Hv_Drv_OsdFont_DrawPointRectangle(USHORT16 usX, USHORT16 usY)
{
    if (usX != 0)
    {
        Hv_Drv_OsdFont_DrawCPUCanvasPoint(s_pstCPUCanvasNode, usX - 1, usY - 1, 0xc8d408);
        Hv_Drv_OsdFont_DrawCPUCanvasPoint(s_pstCPUCanvasNode, usX - 1, usY, 0xc8d408);
        Hv_Drv_OsdFont_DrawCPUCanvasPoint(s_pstCPUCanvasNode, usX - 1, usY + 1, 0xc8d408);
    }

    /* top point */
    Hv_Drv_OsdFont_DrawCPUCanvasPoint(s_pstCPUCanvasNode, usX, usY - 1, 0xc8d408);
    /* current point */
    Hv_Drv_OsdFont_DrawCPUCanvasPoint(s_pstCPUCanvasNode, usX, usY, 0xc8d408);
    Hv_Drv_OsdFont_DrawCPUCanvasPoint(s_pstCPUCanvasNode, usX, usY + 1, 0xc8d408);

    /* bottom point */
    Hv_Drv_OsdFont_DrawCPUCanvasPoint(s_pstCPUCanvasNode, usX + 1, usY - 1, 0xc8d408);
    Hv_Drv_OsdFont_DrawCPUCanvasPoint(s_pstCPUCanvasNode, usX + 1, usY + 1, 0xc8d408);
    /* right point */
    Hv_Drv_OsdFont_DrawCPUCanvasPoint(s_pstCPUCanvasNode, usX + 1, usY, 0xc8d408);

    return;
}

static VOID Hv_Drv_OsdFont_DrawPointDoubleCross(USHORT16 usX, USHORT16 usY)
{
    if (usX != 0)
    {
        Hv_Drv_OsdFont_DrawCPUCanvasPoint(s_pstCPUCanvasNode, usX - 1, usY, 0xc8d408);
        Hv_Drv_OsdFont_DrawCPUCanvasPoint(s_pstCPUCanvasNode, usX - 1, usY + 1, 0xc8d408);
    }

    Hv_Drv_OsdFont_DrawCPUCanvasPoint(s_pstCPUCanvasNode, usX, usY - 1, 0xc8d408);
    Hv_Drv_OsdFont_DrawCPUCanvasPoint(s_pstCPUCanvasNode, usX, usY, 0xc8d408);
    Hv_Drv_OsdFont_DrawCPUCanvasPoint(s_pstCPUCanvasNode, usX, usY + 1, 0xc8d408);
    Hv_Drv_OsdFont_DrawCPUCanvasPoint(s_pstCPUCanvasNode, usX, usY + 2, 0xc8d408);

    Hv_Drv_OsdFont_DrawCPUCanvasPoint(s_pstCPUCanvasNode, usX + 1, usY - 1, 0xc8d408);
    Hv_Drv_OsdFont_DrawCPUCanvasPoint(s_pstCPUCanvasNode, usX + 1, usY, 0xc8d408);
    Hv_Drv_OsdFont_DrawCPUCanvasPoint(s_pstCPUCanvasNode, usX + 1, usY + 1, 0xc8d408);
    Hv_Drv_OsdFont_DrawCPUCanvasPoint(s_pstCPUCanvasNode, usX + 1, usY + 2, 0xc8d408);

    Hv_Drv_OsdFont_DrawCPUCanvasPoint(s_pstCPUCanvasNode, usX + 2, usY, 0xc8d408);
    Hv_Drv_OsdFont_DrawCPUCanvasPoint(s_pstCPUCanvasNode, usX + 2, usY + 1, 0xc8d408);
    return;
}

VOID Hv_Drv_OsdFont_CPUCanvasStep()
{
    UCHAR8 ucIndex = 0;
    USHORT16 usTotalPoints = 400;
    struct LinkedList *pstNode = NULL;
    OsdPoint *pstPoint = NULL;
    SHORT16 sY = 36;
    USHORT16 usX = 0;
    SHORT16 sDelta = 0;
    static BOOL bIncrease = 1;
    static struct LinkedList stAllPoints = {&stAllPoints, &stAllPoints};
    static USHORT16 usPoints = 0;
    static SHORT16 sLastY = 36; /* the middle */

    /* remove one */
    if (usPoints >= usTotalPoints)
    {
        for (ucIndex = 0; ucIndex < 1; ++ucIndex)
        {
            pstNode = Hv_Comm_LinkedList_PopTail(&stAllPoints);
            pstPoint = Hv_Comm_LinkedList_Entry(pstNode, OsdPoint, stEntry);
            HV_FREE(pstPoint);
            pstPoint = NULL;

            --usPoints;
        }
    }

#define POS_RANDOM
#ifdef POS_RANDOM
    bIncrease = random() % 2;
    sDelta = random() % 4;
#else
    sDelta = 1;
#endif
    if (bIncrease)
    {
        sY = sLastY + sDelta;
        if (sY >= 70)
        {
            sY = sLastY - sDelta;
#ifndef POS_RANDOM
            bIncrease = 0;
#endif
        }
    }
    else
    {
        sY = sLastY - sDelta;
        if (sY < 2)
        {
            sY = sLastY + sDelta;
#ifndef POS_RANDOM
            bIncrease = 1;
#endif
        }
    }

    /* generate a new point */
    for (ucIndex = 0; ucIndex < 1; ++ucIndex)
    {
        pstPoint = HV_MALLOC(sizeof(*pstPoint));
        HV_MEMSET(pstPoint, 0, sizeof(*pstPoint));
        Hv_Comm_LinkedList_Init(&pstPoint->stEntry);
        pstPoint->usY = sY;

        Hv_Comm_LinkedList_AddHead(&stAllPoints, &pstPoint->stEntry);
        ++usPoints;
    }

    /* clear area */
    Hv_Drv_OsdFont_ClearCPUCanvas(s_pstCPUCanvasNode);

    Hv_Comm_LinkedList_ForEach(&stAllPoints, pstNode)
    {
        pstPoint = Hv_Comm_LinkedList_Entry(pstNode, OsdPoint, stEntry);

        //Hv_Drv_OsdFont_DrawPointRectangle(usX, pstPoint->usY);
        Hv_Drv_OsdFont_DrawPointDoubleCross(usX, pstPoint->usY);

        usX += 1;
    }

    sLastY = sY;

    Hv_Drv_OsdFrame_DrawPost();
    Hv_Drv_OsdFrame_SetSync();
}
#endif

#endif

#if (HV_PROJECT_CONFIG_OSD_BIGFONT_SUPPORT == HV_CONFIG_ON)
VOID Hv_Drv_OsdFont_TestBigFont(VOID)
{
    OsdAsciiString stAscii = {0};
    OsdWindow stWindow = {0};
    OsdWindowProp stWindowProp = {0};
    OsdNodeHead *pstHead = &stWindow.stFontShowListHead;
    OsdComponentNode stNode = {0};
    USHORT16 usValue = 0;
    USHORT16 usTmp = 0;
    UCHAR8 ucIndex = 0;

    stAscii.usX = 0;
    stAscii.usY = 0;
    stAscii.usIsBigIconFont = HV_TRUE;
    stAscii.ucColorIndex = 4;
    stAscii.ucStringLength = 3;
    stAscii.ucBlockID = 7;

    stNode.pvCpData = &stAscii;
    stNode.stCp.enType = OSD_CP_TYPE_ASCII_STRING;

    pstHead->pstNode = &stNode;

    stWindow.usFontAreaWidth = 800;

    stWindowProp.usX = 300;
    stWindowProp.usY = 300;
    stWindowProp.usWidth= 800;
    stWindowProp.usHeight = 800;

    for (usValue = 0; usValue < 63; ++usValue)
    {
        Hv_Drv_OsdFrame_DrawPre();

        Hv_Drv_OsdWindow_DrawWindow(&stWindowProp);

        usTmp = usValue;
        stAscii.ausString[0] = usTmp / 16 + '0';
        usTmp %= 16;
        stAscii.ausString[1] = usTmp / 4 + '0';
        usTmp %= 4;
        stAscii.ausString[2] = usTmp + '0';

        Hv_Drv_OsdFont_DrawMenuFontList(&stWindow);
        Hv_Drv_OsdFrame_DrawPost();

        Hv_Drv_OsdFrame_SetSync();

        Hv_Vos_MSleep(1000);

        if (usValue == 62)
        {
            usValue = 0;
        }
    }
}

VOID Hv_Drv_OsdFont_TestBigFontBMP(VOID)
{
    OsdAsciiString stAscii = {0};
    OsdWindow stWindow = {0};
    OsdWindowProp stWindowProp = {0};
    OsdNodeHead *pstHead = &stWindow.stFontShowListHead;
    OsdComponentNode stNode = {0};
    USHORT16 usValue = 0;
    USHORT16 usTmp = 0;
    UCHAR8 ucIndex = 0;

    stAscii.usX = 0;
    stAscii.usY = 0;
    stAscii.usIsBigBmpFont = HV_TRUE;
    stAscii.ucColorIndex = 4;
    stAscii.ucStringLength = 3;
    stAscii.ucBlockID = 8;

    stNode.pvCpData = &stAscii;
    stNode.stCp.enType = OSD_CP_TYPE_ASCII_STRING;

    pstHead->pstNode = &stNode;

    stWindow.usFontAreaWidth = 800;

    stWindowProp.usX = 300;
    stWindowProp.usY = 300;
    stWindowProp.usWidth= 800;
    stWindowProp.usHeight = 800;

    for (usValue = 0; usValue < 63; ++usValue)
    {
        Hv_Drv_OsdFrame_DrawPre();

        Hv_Drv_OsdWindow_DrawWindow(&stWindowProp);

        usTmp = usValue;
        stAscii.ausString[0] = usTmp / 16 + '0';
        usTmp %= 16;
        stAscii.ausString[1] = usTmp / 4 + '0';
        usTmp %= 4;
        stAscii.ausString[2] = usTmp + '0';

        Hv_Drv_OsdFont_DrawMenuFontList(&stWindow);
        Hv_Drv_OsdFrame_DrawPost();

        Hv_Drv_OsdFrame_SetSync();

        Hv_Vos_MSleep(1000);

        if (usValue == 62)
        {
            usValue = 0;
        }
    }
}
#endif

#endif
