Hi3559编译Freetype+SDL+SDL_TTF并实现OSD字符叠加
Hi3559编译Freetype+SDL+SDL_TTF并实现OSD字符叠加0. 准备工作在https://github.com/As772309423/freetype-SDL-SDL_TTL上下载freetype-2.4.10、SDL-1.2.15和SDL_ttf-2.0.11,并在虚拟机上进行解压。1. 编译Freetype、SDL和SDL_TTF1.1 Freetype2的编译解压Free
Hi3559编译Freetype+SDL+SDL_TTF并实现OSD字符叠加
0. 准备工作
在https://github.com/As772309423/freetype-SDL-SDL_TTL上下载freetype-2.4.10、SDL-1.2.15和SDL_ttf-2.0.11,并在虚拟机上进行解压。
1. 编译Freetype、SDL和SDL_TTF
1.1 Freetype2的编译
解压Freetype的压缩包,进入源码目录进行配置:
./configure CC=aarch64-himix100-linux-gcc --host=arm-hisiv500-linux --enable-shared=no --enable-static=yes
这里的CC是交叉编译工具链的gcc编译器,enable-shared选项是编译动态库(.so),enable-static是编译静态库(.a),host不用管。
这里按照实际情况设置CC为Hi3559的gcc交叉编译工具aarch64-himix100-linux-gcc
,编译静态库。
执行完毕后,先make,再make install。
make install命令执行中,可以看到生成的头文件位于/usr/local/include/freetype2/freetype
,库文件安装到了/usr/local/lib
。
1.2 SDL库编译
类似1.1,进入目录后,先用和1.1一样的配置命令配置后,make和make install。
最后可以看到库文件安装到了/usr/local/lib
头文件在/usr/local/include/SDL
1.3 SDL_TTF编译
编译SDL_TTF库之前,在SDL_ttf.c
里增加一个动态改变字体大小的接口:
FT_Error TTF_SetFontSize(TTF_Font *font,int ptsize)
{
FT_Fixed scale;
FT_Error error;
FT_Face face;
face = font->face;
/* Make sure that our font face is scalable (global metrics) */
if ( FT_IS_SCALABLE(face) ) {
/* Set the character size and use default DPI (72) */
error = FT_Set_Char_Size( font->face, 0, ptsize * 64, 0, 0 );
if( error ) {
TTF_SetFTError( "Couldn't set font size", error );
TTF_CloseFont( font );
return error;
}
/* Get the scalable font metrics for this font */
scale = face->size->metrics.y_scale;
font->ascent = FT_CEIL(FT_MulFix(face->ascender, scale));
font->descent = FT_CEIL(FT_MulFix(face->descender, scale));
font->height = font->ascent - font->descent + /* baseline */ 1;
font->lineskip = FT_CEIL(FT_MulFix(face->height, scale));
font->underline_offset = FT_FLOOR(FT_MulFix(face->underline_position, scale));
font->underline_height = FT_FLOOR(FT_MulFix(face->underline_thickness, scale));
} else {
/* Non-scalable font case. ptsize determines which family
* or series of fonts to grab from the non-scalable format.
* It is not the point size of the font.
* */
if ( ptsize >= font->face->num_fixed_sizes )
ptsize = font->face->num_fixed_sizes - 1;
font->font_size_family = ptsize;
error = FT_Set_Pixel_Sizes( face,
face->available_sizes[ptsize].height,
face->available_sizes[ptsize].width );
/* With non-scalale fonts, Freetype2 likes to fill many of the
* font metrics with the value of 0. The size of the
* non-scalable fonts must be determined differently
* or sometimes cannot be determined.
* */
font->ascent = face->available_sizes[ptsize].height;
font->descent = 0;
font->height = face->available_sizes[ptsize].height;
font->lineskip = FT_CEIL(font->ascent);
font->underline_offset = FT_FLOOR(face->underline_position);
font->underline_height = FT_FLOOR(face->underline_thickness);
}
if ( font->underline_height < 1 ) {
font->underline_height = 1;
}
font->glyph_italics *= font->height;
Flush_Cache(font);
return 0;
}
在SDL_ttf.h
增加接口的声明:
extern DECLSPEC int SDLCALL TTF_SetFontSize(TTF_Font *font,int ptsize);
之后参考1.2进行编译。
1.4 Demo代码编译
Demo代码如下。
#include <stdio.h>
#include "SDL.h"
#include "SDL_ttf.h"
int main(int argc, const char *argv[])
{
char * pstr = "2019-11-21 15:40:28 Front"; //需要渲染的字符串
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("./AGENCYR.TTF", 64); //字体路径和字体大小
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"); //生成的BMP文件名
SDL_FreeSurface(text);
SDL_FreeSurface(temp);
TTF_CloseFont(font);
TTF_Quit();
return 0;
}
使用如下命令加上对相关库的依赖进行编译:
aarch64-himix100-linux-gcc test.c -o test2 -I/usr/local/include/SDL -I/usr/local/include/freetype2 -L/usr/local/lib/ -Xlinker --start-group -lfreetype -lSDL -lSDL_ttf -lpthread -Wl,--end-group
带库编译时需要注意库和库之间的关联关系。
使用如下命令
aarch64-himix100-linux-gcc test.c -I/usr/local/include/SDL -I/usr/local/include/freetype2 -L/usr/local/lib/ -lfreetype -lSDL -lSDL_ttf -o test
执行失败,提示undefined reference。原因是SDL库依赖freetype库和pthread库,但是gcc的依赖是从右往左。
即:
正确依赖关系:SDL-依赖->freetype & pthread
命令里的依赖关系(错误):freetype-依赖->SDL
解决方法:
根据实际情况修改编译命令:
aarch64-himix100-linux-gcc test.c -I/usr/local/include/SDL -I/usr/local/include/freetype2 -L/usr/local/lib/ -lSDL -lSDL_ttf -lfreetype -lpthread -o test
使用-Xlinker:
aarch64-himix100-linux-gcc test.c -o test2 -I/usr/local/include/SDL -I/usr/local/include/freetype2 -L/usr/local/lib/ -Xlinker "-(" -lfreetype -lSDL -lSDL_ttf -lpthread -Wl,"-)"
或
aarch64-himix100-linux-gcc test.c -o test2 -I/usr/local/include/SDL -I/usr/local/include/freetype2 -L/usr/local/lib/ -Xlinker --start-group -lfreetype -lSDL -lSDL_ttf -lpthread -Wl,--end-group
编译完成后,将程序中的字体放在和生成的文件相同目录下,执行编译后的文件,会看到生成的save.bmp里有渲染出来的文字。
注:
支持中文的方法:
- 选择字体时,选择带中文的字体。
- 渲染使用的函数和程序文件的编码必须统一。如果使用
TTF_RenderUTF8_Solid()
进行带中文字符串渲染,则.c文件必须使用UTF8编码格式。否则需要进行转换,对编码格式进行统一。
2. HI3559的OSD叠加
HI3559调用OSD的流程如下:
2.1 加载本地BMP图像并叠加
代码的逻辑如上图。
本程序是在VDEC的Sample程序上修改,将BMP叠加到VO的设备0通道0上。
基本代码如下:
HI_S32 SAMPLE_Create_Region_VO(VO_CHN Vo_Chn,RGN_HANDLE R_Handle)
{
HI_S32 s32Ret;
MPP_CHN_S stChn;
RGN_ATTR_S stRgnAttr;
RGN_CHN_ATTR_S stChnAttr;
BITMAP_S stBitmap;
//Region Create First
stRgnAttr.enType = OVERLAYEX_RGN;
stRgnAttr.unAttr.stOverlayEx.enPixelFmt = PIXEL_FORMAT_ARGB_1555; //区域的像素格式设置
stRgnAttr.unAttr.stOverlayEx.stSize.u32Width = 496;//128; //叠加区域的宽高,2对齐
stRgnAttr.unAttr.stOverlayEx.stSize.u32Height = 76;//128;
stRgnAttr.unAttr.stOverlayEx.u32BgColor = 0x000003e0; //叠加区域的背景色
stRgnAttr.unAttr.stOverlayEx.u32CanvasNum = 2;
s32Ret = HI_MPI_RGN_Create(R_Handle, &stRgnAttr);
if (s32Ret != HI_SUCCESS)
{
printf("region of chn %d create fail. value=0x%x.", Vo_Chn, s32Ret);
return s32Ret;
}
/*attach region to chn*/
stChn.enModId = HI_ID_VO; //叠加到设备VO上
stChn.s32DevId = 0; //VO的设备ID是0
stChn.s32ChnId = Vo_Chn; //叠加到的设备通道
stChnAttr.bShow = HI_TRUE;
stChnAttr.enType = OVERLAYEX_RGN;
stChnAttr.unChnAttr.stOverlayExChn.stPoint.s32X = 128;//128; //叠加区域的位置
stChnAttr.unChnAttr.stOverlayExChn.stPoint.s32Y = 128;//128;
stChnAttr.unChnAttr.stOverlayExChn.u32BgAlpha = 0; //背景前景透明度
stChnAttr.unChnAttr.stOverlayExChn.u32FgAlpha = 255;
stChnAttr.unChnAttr.stOverlayExChn.u32Layer = 0;
/* load bitmap*/
s32Ret = SampleVoLoadRgnBmp("save.bmp", &stBitmap, HI_FALSE, 0); //读取本地BMP图片
if (s32Ret != HI_SUCCESS)
{
return s32Ret;
}
s32Ret = HI_MPI_RGN_SetBitMap(R_Handle, &stBitmap); //BitMap设置
if (s32Ret != HI_SUCCESS)
{
printf("region set bitmap to Handle %d fail. value=0x%x.", R_Handle, s32Ret);
free(stBitmap.pData);
return s32Ret;
}
free(stBitmap.pData); //BMP设置结束后,释放掉BMP文件的资源
s32Ret = HI_MPI_RGN_AttachToChn(R_Handle, &stChn, &stChnAttr); //Region叠加到通道上
if (s32Ret != HI_SUCCESS)
{
printf("region handle %d attach to chn %d fail. value=0x%x.", R_Handle,Vo_Chn, s32Ret);
return s32Ret;
}
return HI_SUCCESS;
}
HI_S32 SampleVoLoadRgnBmp(const char *filename, BITMAP_S *pstBitmap, HI_BOOL bFil, HI_U32 u16FilColor)
{
OSD_SURFACE_S Surface;
OSD_BITMAPFILEHEADER bmpFileHeader;
OSD_BITMAPINFO bmpInfo;
if(GetBmpInfo(filename,&bmpFileHeader,&bmpInfo) < 0)
{
printf("GetBmpInfo err!\n");
return HI_FAILURE;
}
Surface.enColorFmt = OSD_COLOR_FMT_RGB1555;
pstBitmap->pData = malloc(2*(bmpInfo.bmiHeader.biWidth)*(bmpInfo.bmiHeader.biHeight));
if(NULL == pstBitmap->pData)
{
printf("malloc osd memroy err!\n");
return HI_FAILURE;
}
CreateSurfaceByBitMap(filename,&Surface,(HI_U8*)(pstBitmap->pData));
pstBitmap->u32Width = Surface.u16Width;
pstBitmap->u32Height = Surface.u16Height;
pstBitmap->enPixelFormat = PIXEL_FORMAT_ARGB_1555;
int i,j;
HI_U16 *pu16Temp;
pu16Temp = (HI_U16*)pstBitmap->pData;
if (bFil)
{
for (i=0; i<pstBitmap->u32Height; i++)
{
for (j=0; j<pstBitmap->u32Width; j++)
{
if (u16FilColor == *pu16Temp)
{
*pu16Temp &= 0x7FFF;
}
pu16Temp++;
}
}
}
return HI_SUCCESS;
}
注:
这里的
SampleVoLoadRgnBmp
函数用于读取本地的BMP图片并填充BITMAP_S
结构体。需要sample\common下的bit_map.c和bit_map.h支持。
调用函数的地方在SAMPLE_COMM_VDEC_StartSendStream之后。
SAMPLE_COMM_VDEC_StartSendStream(u32VdecChnNum, &stVdecSend[0], &VdecThread[0]);
//ADD START
printf("Start OSD At R_HNDL 0\n");
const RGN_HANDLE R_Hndl = 0;
SAMPLE_Create_Region_VO(0,R_Hndl);
//ADD END
SAMPLE_COMM_VDEC_CmdCtrl(u32VdecChnNum, &stVdecSend[0], &VdecThread[0]);
//ADD START
SAMPLE_Destroy_VO_OSD(0,R_Hndl);
//ADD END
SAMPLE_COMM_VDEC_StopSendStream(u32VdecChnNum, &stVdecSend[0], &VdecThread[0]);
2.2 调用Freetype&SDL&SDL_TTF进行字符叠加
使用SDL_TTL进行字符叠加的流程和静态显示BMP类似。
不同之处:
- 对字符串进行渲染之前,需要先进行SDL库的初始化和字体的加载。
- 在
HI_MPI_RGN_SetBitMap
函数里用到的Bitmap不再来自文件,而是将SDL_TTF库的渲染结果填入BITMAP_S
结构体。
2.2.1 SDL初始化和Font加载
初始化和加载代码如下:
TTF_Font* InitFonts(const char *ttfFilename,int size)
{
TTF_Font* ret = NULL;
CHK_ISNULL(ttfFilename, NULL);
if(TTF_Init() < 0)
{
printf("Couldn't initialize TTF: %s\n",SDL_GetError());
//SDL_Quit();
return ret;
}
ret = TTF_OpenFont(ttfFilename, size);
if(ret == NULL)
{
printf("Couldn't Open TTF: %s\n",SDL_GetError());
TTF_Quit();
}
return ret;
}
首先调用TTF_Init()
函数初始化SDL_TTF库。
初始化成功后,使用TTF_OpenFont()
打开字体并返回一个TTF_Font
类型的指针。如果打开失败返回NULL。
程序执行完后使用TTF_CloseFont()
和TTF_Quit()
两个函数退出:
void Font_Quit(TTF_Font* font)
{
TTF_CloseFont(font);
TTF_Quit();
}
2.2.2 渲染和叠加
相关代码如下,关键函数已经做了注释。
其中的重点函数是Sample_SurfaceWord_ToBMP()
,实现了Surface
转换成BITMAP_S
的转换。
//OVERLAYEX_MAX_NUM_VO = 1,每个VO最大叠加1个OverlayEx
HI_S32 Sample_String_OSD_Disp(MPP_CHN_S *dstchn,RGN_HANDLE *getHandle,POINT_S pos,const char* str,HI_U32 RgnLayer,const TTF_Font *fnt,SDL_Color color)
{
HI_S32 s32Ret;
//ARG CHECK.
CHK_ISNULL(getHandle,HI_FAILURE);
CHK_ISNULL(str,HI_FAILURE);
CHK_ISNULL(fnt,HI_FAILURE);
//Render Text First
SDL_Surface *text,*temp;
text = TTF_RenderUTF8_Solid(fnt,str,color); //渲染UTF8字符串,需要显示中文要保证这里的str是UTF8编码
CHK_ISNULL(text,HI_FAILURE); //NULL则渲染失败
//Convert PixelFmt
SDL_PixelFormat *fmt;
fmt = (SDL_PixelFormat*)malloc(sizeof(SDL_PixelFormat));
CHK_ISNULL(fmt,HI_FAILURE);
memset(fmt,0,sizeof(SDL_PixelFormat)); //SDL_PixelFormat结构体放置要转换的目的像素格式
fmt->BitsPerPixel = 16; //每个像素16Bit
fmt->BytesPerPixel = 2; //每个像素2Byte
fmt->colorkey = 0xffffffff; //不需要修改
fmt->alpha = 0xff; //不需要修改
temp = SDL_ConvertSurface(text, fmt, 0); //将渲染结果转换成SDL_PixelFormat描述的格式(默认RGB565)
SDL_FreeSurface(text); //释放不适用的Surface
//Create Region if not existed...
if(*getHandle > RGN_HANDLE_MAX)
{
//UNEXIST HANDLE,NOW CREATE ONE.
RGN_ATTR_S stRgnAttr;
memset(&stRgnAttr,0,sizeof(RGN_ATTR_S));
stRgnAttr.enType = OVERLAYEX_RGN;
stRgnAttr.unAttr.stOverlayEx.enPixelFmt = PIXEL_FORMAT_ARGB_1555;
stRgnAttr.unAttr.stOverlayEx.stSize.u32Width = ALIGN_2(temp->w); //使用渲染后的宽高作为区域的宽高,2对齐
stRgnAttr.unAttr.stOverlayEx.stSize.u32Height = ALIGN_2(temp->h);
stRgnAttr.unAttr.stOverlayEx.u32BgColor = 0x000003e0;
stRgnAttr.unAttr.stOverlayEx.u32CanvasNum = 2;
RGN_HANDLE i = Sample_RGN_AllocAvaliable(&stRgnAttr); //循环申请Handle,就是从Handle = 0开始申请
//使用HI_MPI_RGN_Create(Handle,*attr)
//申请失败检查错误码是否为HI_ERR_RGN_EXIST
//是则Handle+1继续直到RGN_HANDLE_MAX
//返回其他错误返回错误码
//申请成功返回成功的Handle
if(i > RGN_HANDLE_MAX)
{
SDL_FreeSurface(temp);
return HI_FAILURE;
}
*getHandle = i;
}
RGN_CHN_ATTR_S stChnAttr;
stChnAttr.bShow = HI_TRUE;
stChnAttr.enType = OVERLAYEX_RGN; //Region类型是OVERLAYEX
stChnAttr.unChnAttr.stOverlayExChn.stPoint.s32X = pos.s32X; //显示OSD的位置
stChnAttr.unChnAttr.stOverlayExChn.stPoint.s32Y = pos.s32Y;
stChnAttr.unChnAttr.stOverlayExChn.u32BgAlpha = 0; //ALPHA=1的前景色透明度(ARGB1555)
stChnAttr.unChnAttr.stOverlayExChn.u32FgAlpha = 255; //ALPHA=0的背景色透明度(ARGB1555)
stChnAttr.unChnAttr.stOverlayExChn.u32Layer = RgnLayer; //区域层次
BITMAP_S stBitmap;
Sample_SurfaceWord_ToBMP(temp,&stBitmap,color); //将渲染后的Surface转换成Bitmap
s32Ret = HI_MPI_RGN_SetBitMap(*getHandle, &stBitmap);
if (s32Ret != HI_SUCCESS)
{
printf("HI_MPI_RGN_SetBitMap failed with %#x!\n", s32Ret);
HI_MPI_RGN_Destroy(*getHandle);
SDL_FreeSurface(temp);
return s32Ret;
}
s32Ret = HI_MPI_RGN_AttachToChn(*getHandle, dstchn, &stChnAttr);
if (s32Ret != HI_SUCCESS)
{
printf("region handle %d attach fail. value=0x%x.", *getHandle, s32Ret);
HI_MPI_RGN_Destroy(*getHandle);
SDL_FreeSurface(temp);
return s32Ret;
}
SDL_FreeSurface(temp);
return HI_SUCCESS;
}
2.2.3 Surface转BITMAP_S
这个函数主要是Surface结构体和BITMAP_S结构体的转换。
主要实现下列功能:
- BITMAP_S结构体的初始化,包括申请内存空间
- 将SDL_TTF的RGB565转换成OSD叠加的ARGB1555像素格式
- 对图像宽度为奇数的Surface进行处理,使之能在宽为偶数的Region上正常显示
代码如下:
HI_VOID Sample_SurfaceWord_ToBMP(SDL_Surface *surface,BITMAP_S *stBitmap,SDL_Color fntcol)
{
HI_U16 wrd_color = ((fntcol.r >> 3) << 11) + ((fntcol.g >> 2) << 5) + ((fntcol.b >> 3) << 0); //RGB888=>RGB565
HI_U16 bg_color = 0xffff - wrd_color; //RGB565下背景色的计算
stBitmap->u32Height = ALIGN_2(surface->h); //BITMAP 的宽高向上2对齐
stBitmap->u32Width = ALIGN_2(surface->w);
stBitmap->pData = malloc(2*(stBitmap->u32Height)*(stBitmap->u32Width)); //申请空间,RGB1555=>2Byte/Pixel,总大小为2*w*h
memset(stBitmap->pData,0,2*(stBitmap->u32Height)*(stBitmap->u32Width));
int i,j;
int w = surface->w;
int h = surface->h;
for (i = 0; i < h; ++i)
{
HI_U16 *p_dst = (HI_U16*)stBitmap->pData;
HI_U16 *p_src = (HI_U16*)surface->pixels;
int dis_pos = 0;
if(w % 2 != 0)
dis_pos = i; //处理w为奇数的情况
for(j=0;j<w;j++)
{
int a,r, g , b;
r = (p_src[i*w+dis_pos+j] & 0xF800) >> 11; //原图像是RGB565,RGB各分量提取
g = (p_src[i*w+dis_pos+j] & 0x07e0) >> 5;
b = (p_src[i*w+dis_pos+j] & 0x001f);
a = (bg_color==p_src[i*w+dis_pos+j])?0:(1<<15); //A分量,计算当前颜色和背景色是否一致
//一致则A位设置为0,透明
p_dst[i*stBitmap->u32Width+j] = (r<<10)+((g>>1)<<5)+b+a; //转换成RGB1555
//剩下一个问题,转换为1555后H265输出颜色有点不正常。转换逻辑没问题,应该是内部显示的效果问题
}
}
stBitmap->enPixelFormat = PIXEL_FORMAT_ARGB_1555;
}
-
申请空间:
目标像素格式RGB1555,每个像素2字节。因此需要申请的内存大小为:
size = 2*w*h
-
源图像宽度为奇数时,由于目标图像宽度向上2对齐,直接复制可能会出现斜体字体的情况。
因此在复制+转换时需要增加一个偏移量。
参考源代码中的
dis_pos
变量。
2.2.4 遇到问题
-
VO无法叠加1个以上的Region,在同一个通道叠加一个以上的region会报错0xA0038009,查看/dev/logmpp会看到类似如下报错:
<3>[region] [Func]:rgn_attach_to_chn [Line]:4344 [Info]:chn(mod:15,dev:0,chn:0)'s region_num is too much!
原因是OVERLAYEX在VO上只能叠加1个Region。
参见
mpp\out\linux\multi-core\include\hi_defines.h
#define OVERLAYEX_MAX_NUM_VO 1
-
图像格式的转换(RGB565 to ARGB1555)
图像格式的转换可以在SDL_ConvertSurface时实现。参考代码如下:
//Convert PixelFmt SDL_PixelFormat *fmt; fmt = (SDL_PixelFormat*)malloc(sizeof(SDL_PixelFormat)); CHK_ISNULL(fmt,HI_FAILURE); memset(fmt,0,sizeof(SDL_PixelFormat)); fmt->BitsPerPixel = 16; fmt->BytesPerPixel = 2; fmt->colorkey = 0xffffffff; fmt->alpha = 0xff; fmt->Amask=0x8000; //Alpha位掩码,即16Bit最高位1000 0000 0000 0000 //fmt->Ashift=15; //fmt->Aloss = 7; fmt->Rmask=0x7C00; //RED 位掩码,0111 1100 0000 0000 //fmt->Rshift=10; //fmt->Rloss = 3; fmt->Gmask=0x03E0; //Green位掩码,0000 0011 1110 0000 //fmt->Gshift= 5; //fmt->Gloss = 3; fmt->Bmask=0x001F; //Blue位掩码,0000 0000 0001 1111 //fmt->Bshift= 0; //fmt->Bloss = 3; temp = SDL_ConvertSurface(text, fmt, 0);
这样转换出来的Surface就是RGB1555格式的数据了。
2.3 直接更新画布
按照SDK文档,除了使用HI_MPI_RGN_SetBitMap()
将BMP图像叠加之外,还可以通过HI_MPI_RGN_GetCanvasInfo()
直接获取画布的内存地址并直接对画布的内存数据进行修改,修改完毕后再使用HI_MPI_RGN_UpdateCanvas()
刷新画布数据。
该方法的效率比调用HI_MPI_RGN_SetBitMap()
高,可以省掉一次内存拷贝。
代码如下:
HI_S32 Sample_SurfaceToCanvas(SDL_Surface *surface,RGN_HANDLE hndl,SDL_Color fntcol)
{
HI_S32 s32Ret = 0;
HI_U16 wrd_color = ((fntcol.r >> 3) << 11) + ((fntcol.g >> 2) << 5) + ((fntcol.b >> 3) << 0); //RGB888=>RGB565
HI_U16 bg_color = 0xffff - wrd_color;
int dstH = ALIGN_2(surface->h);
int dstW = ALIGN_2(surface->w);
int i,j;
int w = surface->w;
int h = surface->h;
RGN_CANVAS_INFO_S cvinfo;
s32Ret = HI_MPI_RGN_GetCanvasInfo(hndl,&cvinfo); //获取画布信息
if(s32Ret != HI_SUCCESS)
{
printf("HI_MPI_RGN_GetCanvasInfo Failed with 0x%08X\n",s32Ret);
return s32Ret;
}
HI_U16 *p_dst = (HI_U16*)cvinfo.u64VirtAddr;
HI_U16 *p_src = (HI_U16*)surface->pixels;
for (i = 0; i < h; ++i)
{
int dis_pos = 0;
if(w % 2 != 0)
dis_pos = i;
for(j=0;j<w;j++)
{
int a,r, g , b;
r = (p_src[i*w+dis_pos+j] & 0xF800) >> 11;
g = (p_src[i*w+dis_pos+j] & 0x07e0) >> 5;
b = (p_src[i*w+dis_pos+j] & 0x001f);
a = (bg_color==p_src[i*w+dis_pos+j])?0:(1<<15);
p_dst[i*(cvinfo.u32Stride/sizeof(HI_U16))+j] = (r<<10)+((g>>1)<<5)+b+a;
/*cvinfo.u32Stride/sizeof(HI_U16):
* cvinfo.u32Stride是每行首地址之间的字节数
* 由于p_dst是16Bit的指针,需要把字节数转换成数组元素个数,即cvinfo.u32Stride/sizeof(HI_U16)
* 图像宽度和stride关系如下:
* --------------------
* | |...|
* | PIC |...|
* | |...|
* --------------------
* |<-Img.Width-->|
* |<---Img.Stride--->|
*/
}
}
s32Ret = HI_MPI_RGN_UpdateCanvas(hndl); //更新内存数据到屏幕
if(s32Ret != HI_SUCCESS)
{
printf("HI_MPI_RGN_UpdateCanvas Failed with 0x%08X\n",s32Ret);
return s32Ret;
}
return HI_SUCCESS;
}
代码和2.2.3中代码基本接近,区别在于2.2.3中Surface是先转换成BITMAP_S,再使用HI_MPI_RGN_SetBitMap()
将数据叠加到区域。而这里是直接对画布的内存数据进行操作。
这里需要注意的是向画布内存区域写入像素数据的代码:
HI_U16 *p_dst = (HI_U16*)cvinfo.u64VirtAddr;
...
p_dst[i*(cvinfo.u32Stride/sizeof(HI_U16))+j] = (r<<10)+((g>>1)<<5)+b+a;
u32Stride
是各行之间首地址的偏差字节。由于我们是以2Byte的格式访问(p_dst是16位指针),因此需要将字节数转换成数组下标,即cvinfo.u32Stride/sizeof(HI_U16)
=cvinfo.u32Stride/2
Image Stride(内存图像行跨度)相关资料节选如下
参考链接:https://blog.csdn.net/jsn_ze/article/details/55190374
2.4 对OSD内容的动态刷新
动态刷新OSD有两种方式,一个是使用SetBmp更新,一个是获取画布进行更新。
获取画布进行更新的方法比较方便,和2.3的代码一样,通过RGN_HANDLE获取到画布的虚拟地址后直接填充,最后在进行UpdateCanvas操作。
2.5 释放和关闭
结束业务后,需要释放相关资源。
使用HI_MPI_RGN_DetachFromChn()
断开region和Channel的链接,再使用HI_MPI_RGN_Destroy()
释放Region的内存。
参考代码:
HI_S32 SAMPLE_Destroy_VO_OSD(VO_CHN Vo_Chn, RGN_HANDLE R_Handle)
{
MPP_CHN_S stChn;
stChn.enModId = HI_ID_VO;
stChn.s32ChnId = Vo_Chn;
stChn.s32DevId = 0;
CHECK_RET(HI_MPI_RGN_DetachFromChn(R_Handle,&stChn),"HI_MPI_RGN_DetachFromChn");
CHECK_RET(HI_MPI_RGN_Destroy(R_Handle),"HI_MPI_RGN_Destroy");
return HI_SUCCESS;
}
3. 在Region任意位置叠加OSD
由于Region的总数是固定的,因此需要在一块区域内显示多块内容。
这种情况下需要申请一块较大的Region并使用更新Canvas的方式对OSD进行更新。
将ARGB1555的OSD叠加到Region上任意位置的代码如下:
//这里的XY是相对于REGION的左上角
//imgdata可以是转成ARGB1555的Surface.pixels,也可以是转换后的BMP.pdata
//imgW/imgH是图像的实际宽高
//startXY是起始点
HI_S32 Sample_Attach1555ImageToExistedCanvas_XY(RGN_HANDLE hndl,void *imgdata,HI_U32 imgW,
HI_U32 imgH,POINT_S startXY,HI_BOOL bUpdateCanvas)
{
HI_S32 s32Ret = 0;
CHK_ISNULL(imgdata,HI_FAILURE);
HI_U16 *posSrc,*posDes;
RGN_CANVAS_INFO_S cvinfo;
s32Ret = HI_MPI_RGN_GetCanvasInfo(hndl,&cvinfo);
if(s32Ret != HI_SUCCESS)
{
printf("HI_MPI_RGN_GetCanvasInfo Failed with 0x%08X\n",s32Ret);
return s32Ret;
}
//检查输入的起始位置是否合法
if(startXY.s32X > cvinfo.stSize.u32Width || startXY.s32Y > cvinfo.stSize.u32Height)
{
HI_MPI_RGN_UpdateCanvas(hndl); //必须进行Update操作,否则该Canvas在执行其他操作时会报错
printf("StartXY Exceed. STARTXY(%d,%d),Canvas Size(W %d,H %d)\n",
startXY.s32X,startXY.s32Y,cvinfo.stSize.u32Width,cvinfo.stSize.u32Height);
return HI_FAILURE;
}
//要复制的图像长宽,如果起始位置合法但是叠加后超出画布范围,则只保留一部分
HI_U32 copyH = (startXY.s32Y+imgH) < cvinfo.stSize.u32Height ? \
imgH:cvinfo.stSize.u32Height-startXY.s32Y;
HI_U32 copyW = (startXY.s32X+imgW) < cvinfo.stSize.u32Width ? \
imgW:cvinfo.stSize.u32Height-startXY.s32X;
//开始复制OSD内容到画布上
int i,j;
posSrc = (HI_U16*)imgdata;
posDes = (HI_U16*)cvinfo.u64VirtAddr;
for(i = startXY.s32Y;i < startXY.s32Y + copyH;i++)
{
memcpy(&(posDes[(cvinfo.u32Stride/2)*i+startXY.s32X]),
&(posSrc[(i-startXY.s32Y)*(imgW+copyW%2)]),
2*copyW);
}
//如果需要更新画布则更新
if(bUpdateCanvas == HI_TRUE)
{
s32Ret = HI_MPI_RGN_UpdateCanvas(hndl);
if(s32Ret != HI_SUCCESS)
{
printf("HI_MPI_RGN_UpdateCanvas Failed with 0x%08X\n",s32Ret);
return s32Ret;
}
}
return HI_SUCCESS;
}
关键步骤代码:
-
计算要叠加显示的图像的实际大小:
//要复制的图像长宽,如果起始位置合法但是叠加后超出画布范围,则只保留一部分 HI_U32 copyH = (startXY.s32Y+imgH) < cvinfo.stSize.u32Height ? \ imgH:cvinfo.stSize.u32Height-startXY.s32Y; HI_U32 copyW = (startXY.s32X+imgW) < cvinfo.stSize.u32Width ? \ imgW:cvinfo.stSize.u32Height-startXY.s32X;
获取到画布大小之后,计算叠加后的图像是否超出画布范围。
如果超出范围,则只复制(0,0)~(cvinfo.stSize.u32Height-startXY.s32X,cvinfo.stSize.u32Height-startXY.s32Y)的图像。
不超出范围,复制完整图像。
-
将图像叠加到起始位置XY处:
int i,j; posSrc = (HI_U16*)imgdata; posDes = (HI_U16*)cvinfo.u64VirtAddr; for(i = startXY.s32Y;i < startXY.s32Y + copyH;i++) { /*复制图解: * ---------------------- * | |...| * | PIC |...| P->Padding,存放图像像素之外的数据 * | x--- |.P.| x->起始点,坐标(startXY.s32X,startXY.s32Y),宽高imgW,imgH * | |**| |...| * | ---- |...| * ---------------------- * |<--Img.Width--->| * |<----Img.Stride---->| * * 如图所示,要把图像内容叠加到Canvas的(startXY.s32X,startXY.s32Y) * 就要把图像逐行填充,从startXY.s32X~(startXY.s32X + imgW),填充行数为imgH * 即内存中 * (cvinfo.u32Stride/2)*(startXY.s32Y+LineNum)+startXY.s32X 到 * (cvinfo.u32Stride/2)*(startXY.s32Y+LineNum)+startXY.s32X + imgW * 其中LineNum是复制的行数 */ memcpy(&(posDes[(cvinfo.u32Stride/2)*i+startXY.s32X]), &(posSrc[(i-startXY.s32Y)*(imgW+copyW%2)]), 2*copyW); }
更多推荐
所有评论(0)