按照手册中的说明,使用区域管理功能需要做如下几步
1.用户填充区域属性并创建区域
2.将该区域指定到具体通道中(如VENC)。在指定到具体通道时,需要输入通道的显示属性
之后用户可以通过以下操作来控制区域属性,以及在某通道的通道显示属性
2.1通过 HI_MPI_RGN_GetAttr、 HI_MPI_RGN_SetAttr 获取和设置区域属性
2.2通过 HI_MPI_RGN_SetBitMap(仅针对 Overlay)设置区域的位图信息
2.3通过 HI_MPI_RGN_GetDisplayAttr 和 HI_MPI_RGN_SetDisplayAttr 获取和设置区域在某通道(如 VENC 通道)的通道显示属性。
2.4最后用户可以将该区域从通道中撤出(非必须操作),再销毁区域。
接下来我们看看每一步应该怎么做
想要创建区域,可以调用函数
HI_MPI_RGN_Create
来创建一个region区域,函数原型如下
HI_S32 HI_MPI_RGN_Create(
RGN_HANDLE Handle, //区域句柄号
const RGN_ATTR_S *pstRegion //区域属性指针
);
每一个区域对应一个句柄,和一个具体的属性RGN_ATTR_S,属性结构体定义如下
typedef struct hiRGN_ATTR_S
{
RGN_TYPE_E enType; //区域类型
RGN_ATTR_U unAttr; //区域属性
} RGN_ATTR_S;
区域类型有以下几种
对应的区域属性就有两种
这两个属性的结构体其实是一样的,以OVERLAY_ATTR_S举例,结构体定义如下
typedef struct hiOVERLAY_ATTR_S
{
PIXEL_FORMAT_E enPixelFmt; //OSD的像素格式,目前只支持ARGB1555,ARGB4444,ARGB8888这三种
HI_U32 u32BgColor; //区域的背景色
SIZE_S stSize; //区域的高宽
}OVERLAY_ATTR_S;
将这些属性填充好,调用HI_MPI_RGN_Create函数后,区域就创建好了,之后我们就可以通过区域句柄号进行管理
想要指定到具体通道,可以调用函数
HI_MPI_RGN_AttachToChn
将区域叠加到通道上,该函数原型为
HI_S32 HI_MPI_RGN_AttachToChn(
RGN_HANDLE Handle, //区域句柄号
const MPP_CHN_S *pstChn, //通道结构体指针
const RGN_CHN_ATTR_S *pstChnAttr //区域通道显示属性指针
);
区域句柄我们已经有了,通道结构体指针定义如下
typedef struct hiMPP_CHN_S
{
MOD_ID_E enModId; //模块号
HI_S32 s32DevId; //设备号
HI_S32 s32ChnId; //通道号
} MPP_CHN_S;
其实意思就是告诉系统,这个区域是叠加在哪个模块的哪个设备的哪个通道上面的
区域通道显示属性指针就是用来设定区域显示属性的,结构体定义如下
typedef struct hiRGN_CHN_ATTR_S
{
HI_BOOL bShow; //区域是否显示
RGN_TYPE_E enType; //区域类型(OVERLAY_RGN,COVER_RGN,COVEREX_RGN,OVERLAYEX_RGN)
RGN_CHN_ATTR_U unChnAttr; //区域通道显示属性(对应上面的四种类型,这个属性也有四种,具体请查阅手册)
} RGN_CHN_ATTR_S;
创建区域并指定到具体的通道中之后,我们的区域就已经完成了
接下来我们就可以通过其他操作在区域里面进行显示了
比如叠加层次,位图填充,叠加透明度等等(表8-2 Hi3516A/Hi3518EV200 region 支持的功能)
这里给出一个示例Demo如下
HI_S32 regions_Create( RGN_HANDLE RgnHandle, int width, int height, int position_x, int position_y )
{
HI_S32 s32Ret = HI_FAILURE;
RGN_ATTR_S stRgnAttr;
MPP_CHN_S stChn;
RGN_CHN_ATTR_S stChnAttr;
/****************************************
step 1: create overlay regions
****************************************/
stRgnAttr.enType = OVERLAYEX_RGN;
stRgnAttr.unAttr.stOverlay.enPixelFmt = PIXEL_FORMAT_RGB_1555;
stRgnAttr.unAttr.stOverlay.stSize.u32Width = width;
stRgnAttr.unAttr.stOverlay.stSize.u32Height = height;
stRgnAttr.unAttr.stOverlay.u32BgColor = 0x1f;
RgnHandle = 0;///*域操作句柄*/
s32Ret = HI_MPI_RGN_Create(RgnHandle, &stRgnAttr);
if (HI_SUCCESS != s32Ret)
{
SAMPLE_PRT("HI_MPI_RGN_Create (%d) failed with %#x!\n", RgnHandle, s32Ret);
return HI_FAILURE;
}
/****************************************
step 2: attach to VPSS
****************************************/
stChn.enModId = HI_ID_VPSS;//HI_ID_VENC;//HI_ID_GROUP;
stChn.s32DevId = 0;
stChn.s32ChnId = 0;
memset(&stChnAttr, 0, sizeof(stChnAttr));
stChnAttr.bShow = HI_TRUE;
stChnAttr.enType = OVERLAYEX_RGN;
stChnAttr.unChnAttr.stOverlayExChn.stPoint.s32X = position_x; //通道画面中显示的位置(x)
stChnAttr.unChnAttr.stOverlayExChn.stPoint.s32Y = position_y; //通道画面中显示的位置(Y)
stChnAttr.unChnAttr.stOverlayExChn.u32BgAlpha = 0; //OSD Alpha
stChnAttr.unChnAttr.stOverlayExChn.u32FgAlpha = 128;
stChnAttr.unChnAttr.stOverlayExChn.u32Layer = 0;
s32Ret = HI_MPI_RGN_AttachToChn(RgnHandle, &stChn, &stChnAttr);
if (HI_SUCCESS != s32Ret) {
SAMPLE_PRT("HI_MPI_RGN_AttachToChn (RgnHandle:%d, chnid:%d) failed with %#x!\n", RgnHandle, stChn.s32ChnId , s32Ret);
return HI_FAILURE;
}
return HI_TRUE;
}
事实上我们创建了区域以后,需要给我们的区域填充一些内容,常用的就是位图填充了,下面我们看看如何进行位图填充
Hi3516A的OSD只能使用位图来进行填充,如果你有一张图片,比如logo之类的,把它做成bmp格式的图片
然后调用
HI_S32 SAMPLE_RGN_LoadBmp
函数先读取位图文件信息,函数原型如下
HI_S32 SAMPLE_RGN_LoadBmp(
const char *filename, //文件路径
BITMAP_S *pstBitmap, //位图属性指针,打开的位图属性将会存放在这里
HI_BOOL bFil, //暂时不关注
HI_U32 u16FilColor //暂时不关注
)
位图文件的属性有了,我们可以调用
HI_MPI_RGN_SetBitMap
函数在区域里面进行位图填充,函数原型如下
HI_S32 HI_MPI_RGN_SetBitMap(
RGN_HANDLE Handle, //区域句柄号
const BITMAP_S *pstBitmap //位图属性指针,就是我们要进行填充的位图信息
);
调用之后,系统就会在根据我们之前设置的区域属性,在对应模块对应设备对应通道上面进行填充
示例Demo如下
HI_S32 update_regions(const char *filename, RGN_HANDLE RgnHandle, const BITMAP_S *stBitmap)
{
HI_S32 s32Ret = HI_FAILURE;
if( HI_SUCCESS != SAMPLE_RGN_LoadBmp(filename, stBitmap, HI_FALSE, 0))
{
SAMPLE_PRT("load bmp failed\n");
return HI_FAILURE;
}
s32Ret = HI_MPI_RGN_SetBitMap(RgnHandle, stBitmap);
if (s32Ret != HI_SUCCESS) {
SAMPLE_PRT("HI_MPI_RGN_SetBitMap (RgnHandle:%d) failed with %#x!\n", RgnHandle, s32Ret);
return HI_FAILURE;
}
return HI_TRUE;
}
有时候我们需要在图像上动态更新时间,码率等等信息,这时候如果都使用bmp图片,肯定不太合适
那就需要把字库的点阵信息转化更位图信息,以英文字符举例
FILE *asc16;
asc16 = fopen(asc16File, "rb");
if (asc16 == NULL )
{
dpf("open asc16 error!\n");
return HI_FAILURE;
}
以8 * 16的字库举例,字库里面的8 * 16代表有8 * 16个点,但是我们要转化成位图信息就意味着,
我们要把字库中的每个点转化为RGB格式的像素点,如果使用RGB1555格式的像素点,
那点阵中的每一个需要占用1 + 5 + 5 + 5 = 16Bit共2个字节的大小,所以每一个字需要分配
2 * 8 * 16大小的空间,一共有ASCNum个字就需要2 * 8 * 16 * ASCNum大小的空间
char *FontDatabase = NULL;
FontDatabase = malloc((2 * 8 * 16) * ASCNum);
if (FontDatabase == NULL )
{
dpf("do malloc error!\n");
return HI_FAILURE;
}
我们知道ASCII码可以显示的字符范围是‘ ’ ~ ‘~’,其他的是特殊字符
所以我们需要从‘ ’开始的位置一直读取到‘~’位置为止,并将每一个点阵点转化为像素点,使用RGB1555格式
HI_U16 *memp;
fseek(asc16, ' ' * 16, SEEK_SET); //找到‘ ’开始的位置,8 * 16点阵中每一个字占用16字节的空间
memp = (HI_U16*) FontDatabase;
for (i = 0; i < ASCNum * 16; i++) //遍历所有字
{
ch = fgetc(asc16); //每次读取一行
//显示一个字节
for (j = 0; j < 8; j++, shortp++) //遍历一行里面的所有点,如果是1,则转化为黑色((HI_U16)0x8000),是0则转化为白色((HI_U16)0x7fff)
{
if (ch & (0x01 << (7 - j)))
*memp = BlackPot;
else
*memp = WhitePot;
}
}
由之前的信息我们知道,想要叠加位图需要调用函数
HI_MPI_RGN_SetBitMap
这个函数原型如下
HI_S32 HI_MPI_RGN_SetBitMap(
RGN_HANDLE Handle, //区域句柄号
const BITMAP_S *pstBitmap //位图属性指针
);
区域句柄号我们创建的时候已经有了,现在需要的就是填充这个位图信息了,这个结构体原型如下
typedef struct hiBITMAP_S
{
PIXEL_FORMAT_E enPixelFormat; //像素格式,比如555,565,1555等
HI_U32 u32Width; //图像宽度
HI_U32 u32Height; //图像高度
HI_VOID* pData; //位图数据
} BITMAP_S;
假设我们需要显示count个字,代码应该大体是这样的
void make_bitmap(const char *osdString, BITMAP_S *pstBitmap)
{
int locations[128] = {0}; //存储字模的位置
int count = 0; //需要显示count个字
int i = 0;
int j = 0;
pstBitmap->enPixelFormat = PIXEL_FORMAT_RGB_1555; //像素格式为RGB1555
pstBitmap->u32Width = 8 * count //每个字的宽度依然是8个像素点
pstBitmap->u32Height = 16 //每行的高度依然是16个像素点
pstBitmap->pData = malloc(2 * 8 * 16 * count); //点阵点转化为像素点需要的空间
memset(pstBitmap->pData, 0, 2 * 8 * 16 * count);
//获取要显示的字的个数和在字模中的位置
for (i = 0; osdString[i] != '\0'; i++)
{
if (osdString[i] >= ' ' && osdString[i] <= '~')
{
locations[count] = osdString[i] - ' ';
count++;
}
}
//遍历所有要显示的字
for (i = 0; i < count; i++)
{
//遍历每一行,一次拷贝一行即8 * 2个字节(每一个点阵点转化为像素点后占用2个字节存储)
for (j = 0; j < 16; j++)
{
memcpy(
//展开后对应存储像素点的第j行的起始位置
pstBitmap->pData + j * pstBitmap->u32Width * 2,
//展开后对应存储点阵点的第i个字符第j行的起始位置
memp + (locations[i] * 2 * 8 * 16) + (16 * j),
16
);
}
}
}
填充好位图信息后,我们调用HI_MPI_RGN_SetBitMap函数进行设置即可。
汉字字库的方式其实是一样的,只不过汉字的位置是由区码加位码的形式来查找的,字库相关的知识可以参考HZK16汉字16*16点阵字库的使用及示例程序
而一个16 * 16汉字点阵转化为像素点需要16 * 16 * 2的空间
所以如果想把一个hzk16中的汉字点阵转化为像素,代码应该是这样的
HI_U16 *memp;
fseek(hzk16, location * 32, SEEK_SET); //location代表汉字的区码+位码,16 * 16点阵中每一个字占用32字节的空间
memp = (HI_U16*) FontDatabase;
for (i = 0; i < 32; i++) //遍历这32个字节
{
ch = fgetc(hzk16); //每次读取一个字节
//显示一个字节
for (j = 0; j < 8; j++) //遍历字节,如果是1,则转化为黑色((HI_U16)0x8000),是0则转化为白色((HI_U16)0x7fff)
{
if (ch & (0x01 << (7 - j)))
*memp = BlackPot;
else
*memp = WhitePot;
}
}
如果要显示count个汉字,代码大体应该是这样的
void make_bitmap(const char *osdString, BITMAP_S *pstBitmap)
{
int locations[128] = {0}; //存储字模的位置
int count = 0; //需要显示count个字
int i = 0;
int j = 0;
pstBitmap->enPixelFormat = PIXEL_FORMAT_RGB_1555; //像素格式为RGB1555
pstBitmap->u32Width = 16 * count //每个字的宽度依然是16个像素点
pstBitmap->u32Height = 16 //每行的高度依然是16个像素点
pstBitmap->pData = malloc(2 * 16 * 16 * count); //点阵点转化为像素点需要的空间
memset(pstBitmap->pData, 0, 2 * 16 * 16 * count);
//获取要显示的字的个数和在字模中的位置
for (i = 0; osdString[i] != '\0'; i++)
{
if (osdString[i] >= 0xa1 && osdString[i + 1] >= 0xa1)
{
locations[osdLen] = (osdString[i] - 0xa1) * 94 + (osdString[i + 1] - 0xa1);
count++;
i++;
}
}
//遍历所有要显示的字
for (i = 0; i < count; i++)
{
//遍历每一行,一次拷贝一行即16 * 2个字节(每一个点阵点转化为像素点后占用2个字节存储)
for (j = 0; j < 16; j++)
{
memcpy(
//展开后对应存储像素点的第j行的起始位置
pstBitmap->pData + j * pstBitmap->u32Width * 2,
//展开后对应存储点阵点的第i个字符第j行的起始位置
memp + (locations[i] * 2 * 16 * 16) + (32 * j),
32
);
}
}
}
同样的,填充好位图信息后,我们调用HI_MPI_RGN_SetBitMap函数进行设置即可。