海思3516a OSD freetype SDL SDL_ttf完成OSD文字添加功能

需求: 在海思3516a芯片上, 显示字幕. 效果图如下:

海思3516a OSD freetype SDL SDL_ttf完成OSD文字添加功能_第1张图片

 

一, 首先参考这篇博文:https://blog.csdn.net/whereisdog/article/details/82769222 下载并编译freetype sdl sdl_ttf.根据此博文介绍, 你可以很快的完成移植工作. 

#include
#include "SDL.h"
#include "SDL_ttf.h"

int main(int argc, const char *argv[])
{
    char * pstr = "hello";
    SDL_PixelFormat *fmt;
    TTF_Font *font;  
    SDL_Surface *text, *temp;  

    if (TTF_Init() < 0 ) 
    {  
        fprintf(stderr, "Couldn't initialize TTF: %s\n",SDL_GetError());  
        SDL_Quit();
    }  

    font = TTF_OpenFont("./simhei.ttf", 48); 
    if ( font == NULL ) 
    {  
        fprintf(stderr, "Couldn't load %d pt font from %s: %s\n",18,"ptsize", SDL_GetError());  
    }  

    SDL_Color forecol = { 0xff, 0xff, 0xff, 0xff };  
    text = TTF_RenderUTF8_Solid(font, pstr, forecol);

    fmt = (SDL_PixelFormat*)malloc(sizeof(SDL_PixelFormat));
    memset(fmt,0,sizeof(SDL_PixelFormat));
    fmt->BitsPerPixel = 16;
    fmt->BytesPerPixel = 2;
    fmt->colorkey = 0xffffffff;
    fmt->alpha = 0xff;

    temp = SDL_ConvertSurface(text,fmt,0);
    SDL_SaveBMP(temp, "save.bmp"); 

    SDL_FreeSurface(text);  
    SDL_FreeSurface(temp);
    TTF_CloseFont(font);  
    TTF_Quit();  

    return 0;
}

二, 介绍一下我所遇到的坑. 

1. 应用库的添加. 

LIBS += SDL_ttf_2.0.11_out_3516A/lib/libSDL_ttf.a  (这个要放在前面, 不然会出现链接不到函数的错误)
LIBS += SDL_1.2.15_out_3516A/lib/libSDL.a
LIBS += freetype_2.4.10_out_3516A/lib/libfreetype.a

2. 保存的图片如果宽高为奇数,如(63,41) 则HI_MPI_RGN_Create报错(0xA0038003 HI_ERR_RGN_ILLEGAL_PARAM 参数超出合法范围)

但是, 用户设置的文字内容长度以及字体大小是不可控的. 所以生成的图片不可能避免会产生奇数宽高, 此时怎么办呢?办法很简单. 直接上代码吧:

   BITMAP_S stBitmap;

    //因为要把文字镂空部份透明出来, 所以必须使用rgb1555 (1表示alpha值).   这里很重要. 是下面遇坑的伏笔. 
    stBitmap.enPixelFormat = PIXEL_FORMAT_RGB_1555;
    stBitmap.u32Width = w;
    stBitmap.u32Height = h;
    if (stBitmap.u32Width % 2 != 0)
    {
        stBitmap.u32Width += 1;
    }

    if (stBitmap.u32Height % 2 != 0)
    {
        stBitmap.u32Height += 1;
    }

    //分配内存
    stBitmap.pData = malloc(2 * (stBitmap.u32Width) * (stBitmap.u32Height));
    memset(stBitmap.pData, 0, 2 * (stBitmap.u32Width) * (stBitmap.u32Height));

就是说如果遇到了奇数宽高的情况, 我们就人为的给他加1个像素, 使其满足HI_MPI_RGN_Create需要偶数作为参数的需求. 

然后是将sdl生成的bmp图片内容复制过来. 这里有2个坑, 一是尺寸,二是颜色问题. 首先来讲尺寸. 

我们知道16位的bmp图片. 他的内存长度应该为: len = w*h*2; 一个像素点由2个字节表示. 当sdl生成的图片src_bmp.宽高为偶数的时候, 象下面这样做就可以把内容拷贝过去了. 

char* p_src = (char*)temp->pixels;//前文中生成的bmp图片.

char* p_des = (char*)stBitmap.pData; //海思结构体BITMAP_S 也在前文中分配的. 

int w = temp->w;

int h = temp->h;

for (i = 0; i < h; ++i)
{
       memcpy(p_des + stBitmap.u32Width*2, p_src + w*2, w*2);
}

至此, 如果你生成的图片恰好是偶数的, 那么你可以成功显示出来了. 但如果为奇数的话, 你会发现在编码流上的图片是斜着的. 

解决办法如下:(我意外发现是这样的).

for (i = 0; i < h; ++i)
{

       int dis_pos = 0;

       //这里的w ==> psrc_w;
       if(w % 2 != 0)
            dis_pos = i*2;
       memcpy(p_des + stBitmap.u32Width*2, p_src + w*2 + dis_pos, w*2);
}

好了, 现在不管是奇数还是偶数的图片都能正常显示了. 

但是你的老板说, 我们可以设置字体颜色. 你设置白色的时候.是正确的. 

见前文 SDL_Color forecol = { 0xff, 0xff, 0xff, 0xff };  前面3个参数分别代表(rgb最后一个未使用. 

当你设置其它颜色的时候, 你会发现显示在编码流上的图片和你设置的不一样. 这个坑我搞了很久, 才找到原因. 

首先参考一个博客:https://blog.csdn.net/qq_26671365/article/details/79007066 其实这个博客里.作者说错了. 真实情况是什么呢?

sdl生成的图片格式默认是 rgb565 r占5位, 然后g占6位. b占最后5位. 没有alpha值. 

而海思的Bitmap里面的存储方式是可选的. 如果你要用:PIXEL_FORMAT_RGB_1555 带alpha值. 因为字体镂空的地方你需要设置透明. 这个时候颜色就对不上了啊. 所以你要一个像素一个像素的提取出来, 分别计算rgb值 再转成 PIXEL_FORMAT_RGB_1555;代码如下:

for (i = 0; i < h; ++i)
    {
        int dis_pos = 0;
        if(w % 2 != 0)
            dis_pos = i*2;

        for(j = 0; j < w*2; j += 2)
        {
            //rgb(565)--
            int a;
            int r, g , b;
            unsigned short src_color;
            memcpy((char*)&src_color, p_src + w*i*2 + dis_pos + j, 2);
            r = (src_color & 0xF800) >> 11;
            g = (src_color & 0x07e0) >> 5;
            b = (src_color & 0x001f);
            
            a = 1 << 15;
            if (bck_color == src_color)
                a = 0;
            
            r = r << 10;
            g = (g >> 1) << 5;
            b = b;

            //argb-->
            unsigned short des_color = a + r + g + b;
            int color1 = des_color & 0x00ff;
            int color2 = (des_color & 0xff00) >> 8;

            p_des[i*stBitmap.u32Width*2 + j] = color1;
            p_des[i*stBitmap.u32Width*2 + j + 1] = color2;

        }
    }

这里说一下. bck_color. SDL在生成图片时需要设置文字颜色,这里我们称为 words_color;然后文字以外的镂空部分, 称为bck_color. 它是怎么计算的呢. 如下:

unsigned short words_color = ((r >> 3) << 11) + ((g >> 2) << 5) + (b >> 3);
unsigned short bck_color = 0xffff - words_color;

好了, 至此.就完成功能了. 下面我贴一下我的代码:

int SetWords(unsigned short bck_color, void * buff, int w, int h)
{
    HI_S32 s32Ret;
    MPP_CHN_S stChn;
    RGN_ATTR_S stRgnAttrSet;
    RGN_CHN_ATTR_S stChnAttr;
    BITMAP_S stBitmap;
    
    
    char tv_words_xpos[20];
    char tv_words_ypos[20];
    
    read_profile_string("TV_WORDS", "x", tv_words_xpos, 20, "200", CONFIG_FILE_PATH);
    read_profile_string("TV_WORDS", "y", tv_words_ypos, 20, "0", CONFIG_FILE_PATH);    

    int osd_x = atoi(tv_words_xpos);
    int osd_y = atoi(tv_words_ypos);
    
    stBitmap.enPixelFormat = PIXEL_FORMAT_RGB_1555;
    stBitmap.u32Width = w;
    stBitmap.u32Height = h;
    if (stBitmap.u32Width % 2 != 0)
    {
        stBitmap.u32Width += 1;
    }

    if (stBitmap.u32Height % 2 != 0)
    {
        stBitmap.u32Height += 1;
    }

    //分配内存
    stBitmap.pData = malloc(2 * (stBitmap.u32Width) * (stBitmap.u32Height));
    memset(stBitmap.pData, 0, 2 * (stBitmap.u32Width) * (stBitmap.u32Height));
    int i,j;

    char* p_des = (char*)stBitmap.pData;
    char* p_src = (char*)buff;

    int flag = 0;
    //unsigned short* p_short_src = (unsigned short*)buff;
    for (i = 0; i < h; ++i)
    {
        int dis_pos = 0;
        if(w % 2 != 0)
            dis_pos = i*2;

        for(j = 0; j < w*2; j += 2)
        {
            //rgb(565)--
            int a;
            int r, g , b;
            unsigned short src_color;
            memcpy((char*)&src_color, p_src + w*i*2 + dis_pos + j, 2);
            r = (src_color & 0xF800) >> 11;
            g = (src_color & 0x07e0) >> 5;
            b = (src_color & 0x001f);
            
            a = 1 << 15;
            if (bck_color == src_color)
                a = 0;
            
            r = r << 10;
            g = (g >> 1) << 5;
            b = b;

            //argb-->
            unsigned short des_color = a + r + g + b;
            int color1 = des_color & 0x00ff;
            int color2 = (des_color & 0xff00) >> 8;

            p_des[i*stBitmap.u32Width*2 + j] = color1;
            p_des[i*stBitmap.u32Width*2 + j + 1] = color2;

        }
    }


    RGN_HANDLE Handle = 1; 
    stRgnAttrSet.enType = OVERLAYEX_RGN;
    stRgnAttrSet.unAttr.stOverlayEx.enPixelFmt       = PIXEL_FORMAT_RGB_1555;
    stRgnAttrSet.unAttr.stOverlayEx.stSize.u32Width  = stBitmap.u32Width;
    stRgnAttrSet.unAttr.stOverlayEx.stSize.u32Height = stBitmap.u32Height;
    stRgnAttrSet.unAttr.stOverlayEx.u32BgColor       = 0x000003e0;

//    printf("w:%d  h: %d\n", w, h);
//    printf("w:%d  h: %d\n", stBitmap.u32Width, stBitmap.u32Height);
    
    HI_MPI_RGN_Create(Handle, &stRgnAttrSet);
    HI_MPI_RGN_SetBitMap(Handle,&stBitmap);
    
    /*attach the OSD to the vpss*/
    stChn.enModId  = HI_ID_VPSS;
    stChn.s32DevId = 0;
    stChn.s32ChnId = VPSS_CHN3;
    
    stChnAttr.bShow  = HI_TRUE;
    stChnAttr.enType = OVERLAYEX_RGN;
    stChnAttr.unChnAttr.stOverlayExChn.stPoint.s32X = osd_x;
    stChnAttr.unChnAttr.stOverlayExChn.stPoint.s32Y = osd_y;
    stChnAttr.unChnAttr.stOverlayExChn.u32BgAlpha   = 0;
    stChnAttr.unChnAttr.stOverlayExChn.u32FgAlpha   = 255;
    stChnAttr.unChnAttr.stOverlayExChn.u32Layer     = 0;

    for (int i = 0; i < 4; ++i)
       {
           stChn.s32ChnId = i;
           s32Ret = HI_MPI_RGN_AttachToChn(Handle, &stChn, &stChnAttr);
        if(s32Ret != HI_SUCCESS)
        { 
            //printf("\n===========%d========HI_MPI_RGN_AttachToChn error %x\n", i, s32Ret);
            return s32Ret;
        }
    }

    //释放内存
    free(stBitmap.pData);

    return 0;
}

int StartWordsOSD()
{
    char tv_words_enable[20];
    char tv_words_size[20];
    char r_buf[20];
    char g_buf[20];
    char b_buf[20];

    char tv_words[1024];
    memset(tv_words, 0, sizeof(tv_words));
    read_profile_string("TV_WORDS", "words", tv_words, sizeof(tv_words), "", CONFIG_FILE_PATH);
    read_profile_string("TV_WORDS", "enable", tv_words_enable, 20, "0", CONFIG_FILE_PATH);    
    read_profile_string("TV_WORDS", "size", tv_words_size, 20, "20", CONFIG_FILE_PATH);
    read_profile_string("TV_WORDS", "r", r_buf, 20, "255", CONFIG_FILE_PATH);    
    read_profile_string("TV_WORDS", "g", g_buf, 20, "255", CONFIG_FILE_PATH);
    read_profile_string("TV_WORDS", "b", b_buf, 20, "255", CONFIG_FILE_PATH);

    if (atoi(tv_words_enable) == 0)
        return 0;

    Uint8 r = atoi(r_buf);
    Uint8 g = atoi(g_buf);
    Uint8 b = atoi(b_buf);
    int font_size = atoi(tv_words_size);
    
    SDL_PixelFormat *fmt;
    TTF_Font *font;
    SDL_Surface *text, *temp;  

    if (TTF_Init() < 0 ) 
    {  
        printf("Couldn't initialize TTF: %s\n",SDL_GetError());  
        SDL_Quit();
        return -1;
    }  

    font = TTF_OpenFont("my.ttf", font_size); 

    printf("osd_words color: r:%d g:%d b:%d\n", r, g, b);

    unsigned short words_color = ((r >> 3) << 11) + ((g >> 2) << 5) + (b >> 3);
     unsigned short bck_color = 0xffff - words_color;

    SDL_Color forecol = { r, g, b, 0xff};  
    
    text = TTF_RenderUTF8_Solid(font, tv_words, forecol);

    fmt = (SDL_PixelFormat*)malloc(sizeof(SDL_PixelFormat));
    memset(fmt,0,sizeof(SDL_PixelFormat));
    fmt->BitsPerPixel = 16;
    fmt->BytesPerPixel = 2;
    fmt->colorkey = 0xffffffff;
    fmt->alpha = 0xff;

    temp = SDL_ConvertSurface(text,fmt,0);
    SetWords(bck_color, temp->pixels, temp->w, temp->h);
    //SDL_SaveBMP(temp, "save.bmp"); 

    SDL_FreeSurface(text);
    SDL_FreeSurface(temp);
    TTF_CloseFont(font);
    TTF_Quit();

    return 0;
}

void CloseWordsOSD()
{
    HI_S32 s32Ret;
    RGN_HANDLE Handle = 1; 
    MPP_CHN_S stChn;
    
    for (int i = 0; i < 4; ++i)
       {
           stChn.enModId  = HI_ID_VPSS;
        stChn.s32DevId = 0;
           stChn.s32ChnId = i;
           s32Ret = HI_MPI_RGN_DetachFromChn(Handle, &stChn);
        if(s32Ret != HI_SUCCESS)
        { 
            printf("\n===========%d========HI_MPI_RGN_DetachFromChn error %x\n", i, s32Ret);
        }
    }

    HI_MPI_RGN_Destroy(Handle);
}

不怎么写博客. 希望可以帮助到大家. 

 

 

你可能感兴趣的:(海思3516a OSD freetype SDL SDL_ttf完成OSD文字添加功能)