• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022 HiSilicon (Shanghai) Technologies CO., LIMITED.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include <stdlib.h>
17 #include <string.h>
18 #include <stdio.h>
19 #include <stdbool.h>
20 
21 #include "hi_comm_vb.h"
22 #include "hi_comm_vgs.h"
23 #include "hi_comm_region.h"
24 #include "mpi_sys.h"
25 #include "mpi_vgs.h"
26 #include "hi_buffer.h"
27 
28 #include "misc_util.h"
29 #include "ai_infer_process.h"
30 #include "vgs_img.h"
31 
32 #ifdef __cplusplus
33 #if __cplusplus
34 extern "C" {
35 #endif
36 #endif /* End of #ifdef __cplusplus */
37 
38 #define VGS_MAX_LINE            100 // The maximum number of lines for a superimposed graphics operation
39 #define RECT_LINES              4 // The number of lines in a rectangle
40 
41 #define RECT_LINE1              1
42 #define RECT_LINE2              2
43 #define RECT_LINE3              3
44 
45 /*
46  * 设置帧的buffer
47  * Set the buf of the frame
48  */
MppFrmSetBuf(VIDEO_FRAME_INFO_S * frm,const VB_CAL_CONFIG_S * vbCfg,HI_U64 phyAddr,uint8_t * virAddr)49 static void MppFrmSetBuf(VIDEO_FRAME_INFO_S* frm,
50     const VB_CAL_CONFIG_S *vbCfg, HI_U64 phyAddr, uint8_t *virAddr)
51 {
52     /*
53      * 目前只支持SP422/SP420,不支持SP444
54      * Currently only SP422/SP420 is supported, SP444 is not supported
55      */
56     frm->stVFrame.u32HeaderStride[0] = vbCfg->u32HeadStride;
57     frm->stVFrame.u32HeaderStride[1] = vbCfg->u32HeadStride;
58     frm->stVFrame.u32HeaderStride[2] = vbCfg->u32HeadStride; // 2: Array subscript, not out of bounds
59     frm->stVFrame.u64HeaderPhyAddr[0] = phyAddr;
60     frm->stVFrame.u64HeaderPhyAddr[1] = frm->stVFrame.u64HeaderPhyAddr[0] + vbCfg->u32HeadYSize;
61     frm->stVFrame.u64HeaderPhyAddr[2] = frm->stVFrame.u64HeaderPhyAddr[1]; // 2: Array subscript, not out of bounds
62     frm->stVFrame.u64HeaderVirAddr[0] = (HI_U64)(HI_UL)virAddr;
63     frm->stVFrame.u64HeaderVirAddr[1] = frm->stVFrame.u64HeaderVirAddr[0] + vbCfg->u32HeadYSize;
64     frm->stVFrame.u64HeaderVirAddr[2] = frm->stVFrame.u64HeaderVirAddr[1]; // 2: Array subscript, not out of bounds
65 
66     frm->stVFrame.u32Stride[0] = vbCfg->u32MainStride;
67     frm->stVFrame.u32Stride[1] = vbCfg->u32MainStride;
68     frm->stVFrame.u32Stride[2] = vbCfg->u32MainStride; // 2: Array subscript, not out of bounds
69     frm->stVFrame.u64PhyAddr[0] = frm->stVFrame.u64HeaderPhyAddr[0] + vbCfg->u32HeadSize;
70     frm->stVFrame.u64PhyAddr[1] = frm->stVFrame.u64PhyAddr[0] + vbCfg->u32MainYSize;
71     frm->stVFrame.u64PhyAddr[2] = frm->stVFrame.u64PhyAddr[1]; // 2: Array subscript, not out of bounds
72     frm->stVFrame.u64VirAddr[0] = frm->stVFrame.u64HeaderVirAddr[0] + vbCfg->u32HeadSize;
73     frm->stVFrame.u64VirAddr[1] = frm->stVFrame.u64VirAddr[0] + vbCfg->u32MainYSize;
74     frm->stVFrame.u64VirAddr[2] = frm->stVFrame.u64VirAddr[1]; // 2: Array subscript, not out of bounds
75 }
76 
77 /*
78  * 创建一个空的frame buf.
79  * 它根据参数的规格计算frame所需空间,为frame分配内存.
80  * @param frm[in|out]: 若成功,创建的frame信息会复制到frm.
81  * @param width[in]: frame width.
82  * @param height[in]: frame height.
83  * @param pixelFormat[in]: 置为-1表示取默认值PIXEL_FORMAT_YVU_SEMIPLANAR_420.
84  * @param bitWidth[in]: 置为-1表示取默认值DATA_BITWIDTH_8.
85  * @param compressMode[in]: 置为-1表示取默认值COMPRESS_MODE_NONE.
86  * @param align[in]: 对齐的字节长度. 置为-1表示取默认值'自动对齐'.
87  *
88  * Create an empty frame buf.
89  * It calculates the space required by the frame according to the parameter specification,
90  * and allocates memory for the frame.
91  * @param frm[in|out]: If successful, the created frame information will be copied to frm.
92  * @param width[in]: frame width.
93  * @param height[in]: frame height.
94  * @param pixelFormat[in]: Set to -1 means to take the default value PIXEL_FORMAT_YVU_SEMIPLANAR_420.
95  * @param bitWidth[in]: Set to -1 means to take the default value DATA_BITWIDTH_8.
96  * @param compressMode[in]: Set to -1 means to take the default value COMPRESS_MODE_NONE.
97  * @param align[in]: The byte length of the alignment. Set to -1 to take the default value 'automatic alignment'.
98  */
MppFrmCreate(VIDEO_FRAME_INFO_S * frm,int width,int height,PIXEL_FORMAT_E pixelFormat,DATA_BITWIDTH_E bitWidth,COMPRESS_MODE_E compressMode,int align)99 int MppFrmCreate(
100     VIDEO_FRAME_INFO_S* frm,
101     int width, int height,
102     PIXEL_FORMAT_E pixelFormat,
103     DATA_BITWIDTH_E bitWidth,
104     COMPRESS_MODE_E compressMode,
105     int align)
106 {
107     HI_ASSERT(frm);
108     VB_CAL_CONFIG_S vbCfg;
109 
110     if (memset_s(frm, sizeof(*frm), 0, sizeof(*frm)) != EOK) {
111         HI_ASSERT(0);
112     }
113 
114     HI_ASSERT(width > 0 && height > 0);
115     if ((int)pixelFormat < 0) {
116         pixelFormat = PIXEL_FORMAT_YVU_SEMIPLANAR_420;
117     }
118     if ((int)bitWidth < 0) {
119         bitWidth = DATA_BITWIDTH_8;
120     }
121     if ((int)compressMode < 0) {
122         compressMode = COMPRESS_MODE_NONE;
123     }
124     if (align < 0) {
125         HI_ASSERT(0);
126     }
127 
128     COMMON_GetPicBufferConfig(width, height, pixelFormat, bitWidth, compressMode, align, &vbCfg);
129 
130     VB_BLK vbHnd = HI_MPI_VB_GetBlock(VB_INVALID_POOLID, vbCfg.u32VBSize, NULL);
131     if (vbHnd == VB_INVALID_HANDLE) {
132         SAMPLE_PRT("HI_MPI_VB_GetBlock FAIL\n");
133         return HI_FAILURE;
134     }
135 
136     HI_U64 phyAddr = HI_MPI_VB_Handle2PhysAddr(vbHnd);
137     HI_ASSERT(phyAddr);
138     uint8_t* virAddr = (uint8_t*)HI_MPI_SYS_Mmap(phyAddr, vbCfg.u32VBSize);
139     HI_ASSERT(virAddr);
140 
141     /*
142      * u64PrivateData用来存储映射的内存区长度和vbHnd,在destroy时会用到
143      *
144      * u64PrivateData is used to store the length of the mapped memory area and vbHnd,
145      * which will be used when destroying
146      */
147     frm->stVFrame.u64PrivateData = ((uint64_t)(uint32_t)vbHnd) << HI_INT32_BITS;
148     frm->stVFrame.u64PrivateData |= (uint64_t)vbCfg.u32VBSize;
149 
150     frm->enModId = HI_ID_VGS;
151     frm->u32PoolId = HI_MPI_VB_Handle2PoolId(vbHnd);
152 
153     frm->stVFrame.u32Width = width;
154     frm->stVFrame.u32Height = height;
155     frm->stVFrame.enField = VIDEO_FIELD_FRAME;
156     frm->stVFrame.enPixelFormat = pixelFormat;
157     frm->stVFrame.enVideoFormat = VIDEO_FORMAT_LINEAR;
158     frm->stVFrame.enCompressMode = compressMode;
159     frm->stVFrame.enDynamicRange = DYNAMIC_RANGE_SDR8;
160     frm->stVFrame.enColorGamut = COLOR_GAMUT_BT601;
161 
162     MppFrmSetBuf(frm, &vbCfg, phyAddr, virAddr);
163     return HI_SUCCESS;
164 }
165 
166 /*
167  * 判断frame是否可用, 即是否分配了内存
168  * Determine whether the frame is available, That is, whether the memory is allocated
169  */
MppFrmValid(const VIDEO_FRAME_INFO_S * frm)170 bool MppFrmValid(const VIDEO_FRAME_INFO_S* frm)
171 {
172     /*
173      * VPSS输出的frame默认没有映射虚地址
174      * The frame output by VPSS does not map virtual addresses by default
175      */
176     return frm->stVFrame.u64PhyAddr[0];
177 }
178 
179 /*
180  * 销毁frame
181  * Destory frame
182  */
MppFrmDestroy(VIDEO_FRAME_INFO_S * frm)183 void MppFrmDestroy(VIDEO_FRAME_INFO_S* frm)
184 {
185     if (!MppFrmValid(frm)) {
186         return;
187     }
188 
189     /*
190      * u64PrivateData被用来存储映射的内存区长度和vbHnd,在创建时设置
191      * u64PrivateData is used to store the length of the mapped memory area and vbHnd, which is set at create
192      */
193     uint32_t memSize = (uint32_t)(frm->stVFrame.u64PrivateData);
194     uint32_t vbHnd = (uint32_t)(frm->stVFrame.u64PrivateData >> HI_INT32_BITS);
195     HI_S32 ret;
196 
197     ret = HI_MPI_SYS_Munmap((void*)(uintptr_t)frm->stVFrame.u64VirAddr[0], memSize);
198     HI_ASSERT(ret == HI_SUCCESS);
199     ret = HI_MPI_VB_ReleaseBlock(vbHnd);
200     HI_ASSERT(ret == HI_SUCCESS);
201     if (memset_s(frm, sizeof(*frm), 0, sizeof(*frm)) != EOK) {
202         HI_ASSERT(0);
203     }
204 }
205 
206 /*
207  * 执行一次VGS缩放
208  * 每一次VGS resize的缩放倍数是有限制的. VGS支持对一幅图像进行缩放,最大支持图像
209  * 宽高均放大16倍,缩小30 倍。支持单分量(Y)缩放
210  *
211  * Perform a VGS resize.
212  * The zoom factor of each VGS resize is limited. VGS supports zooming of an image.
213  * The width and height are both enlarged by 16 times and reduced by 30 times.Support single-component (Y) scaling.
214  */
VgsResizeOnce(const VIDEO_FRAME_INFO_S * src,VIDEO_FRAME_INFO_S * dst,uint32_t dstWidth,uint32_t dstHeight)215 static int VgsResizeOnce(const VIDEO_FRAME_INFO_S* src, VIDEO_FRAME_INFO_S* dst, uint32_t dstWidth, uint32_t dstHeight)
216 {
217     HI_ASSERT(src && dst);
218     HI_ASSERT(dstWidth > 0 && dstHeight > 0);
219     VGS_HANDLE jobHnd = -1;
220     VGS_TASK_ATTR_S task;
221     int ret;
222 
223     ret = MppFrmCreate(dst, dstWidth, dstHeight, src->stVFrame.enPixelFormat, DATA_BITWIDTH_8,
224         src->stVFrame.enCompressMode, 0);
225     if (ret != 0) {
226         SAMPLE_PRT("frm resize FAIL, for create dstFrm FAIL\n");
227         return ret;
228     }
229 
230     if (memset_s(&task, sizeof(task), 0, sizeof(task)) != EOK) {
231         HI_ASSERT(0);
232     }
233     task.stImgIn = *src;
234     task.stImgOut = *dst;
235 
236     ret = HI_MPI_VGS_BeginJob(&jobHnd);
237     if (ret != 0) {
238         SAMPLE_PRT("HI_MPI_VGS_BeginJob FAIL, ret=%08X\n", ret);
239         if (jobHnd >= 0 && HI_MPI_VGS_CancelJob(jobHnd) != HI_SUCCESS) {
240             HI_ASSERT(0);
241         }
242         MppFrmDestroy(dst);
243         return ret;
244     }
245     HI_ASSERT(jobHnd >= 0);
246 
247     ret = HI_MPI_VGS_AddScaleTask(jobHnd, &task, VGS_SCLCOEF_NORMAL);
248     if (ret != 0) {
249         SAMPLE_PRT("HI_MPI_VGS_AddScaleTask FAIL, ret=%08X\n", ret);
250         if (jobHnd >= 0 && HI_MPI_VGS_CancelJob(jobHnd) != HI_SUCCESS) {
251             HI_ASSERT(0);
252         }
253         MppFrmDestroy(dst);
254         return ret;
255     }
256 
257     ret = HI_MPI_VGS_EndJob(jobHnd);
258     if (ret != 0) {
259         SAMPLE_PRT("HI_MPI_VGS_EndJob FAIL, ret=%08X\n", ret);
260         if (jobHnd >= 0 && HI_MPI_VGS_CancelJob(jobHnd) != HI_SUCCESS) {
261             HI_ASSERT(0);
262         }
263         MppFrmDestroy(dst);
264         return ret;
265     }
266     return 0;
267 }
268 
269 /*
270  * 对一帧实现缩放
271  * 多次调用vgs_resize以实现任意比例的缩放
272  * 为简化实现,约定每次缩放最大14倍,此时宽、高仅需2像素对齐
273  * 当两个方向缩放方向不同时,例如一向(如X)放大,另一向缩小倍,无需特别处理
274  * 此时某个方向或两个方向缩放比例均超标,也不需要特别处理
275  *
276  * resize frame.
277  * Call vgs_resize multiple times to achieve arbitrary scaling.
278  * In order to simplify the implementation, it is agreed that each zoom is up to 14 times,
279  * and at this time, the width and height only need to be aligned by 2 pixels.
280  * When the zooming direction of the two directions is different, for example,
281  * zooming in one direction (such as X) and zooming out in the other direction, no special processing is required
282  * At this time, the zoom ratio in one direction or both directions exceeds the standard,
283  * and no special treatment is required.
284  */
MppFrmResize(const VIDEO_FRAME_INFO_S * src,VIDEO_FRAME_INFO_S * dst,uint32_t dstWidth,uint32_t dstHeight)285 int MppFrmResize(
286     const VIDEO_FRAME_INFO_S* src,
287     VIDEO_FRAME_INFO_S* dst,
288     uint32_t dstWidth, uint32_t dstHeight)
289 {
290     static const double rateMax = 14.0; // Maximum magnification
291     static const double rateMin = 1.0 / rateMax; // The smallest magnification, that is, the largest reduction
292 
293     uint32_t srcWidth = src->stVFrame.u32Width;
294     uint32_t srcHeight = src->stVFrame.u32Height;
295     HI_ASSERT(srcWidth > 0 && srcHeight > 0);
296     HI_ASSERT(!(srcWidth % HI_OVEN_BASE) && !(srcHeight % HI_OVEN_BASE));
297     HI_ASSERT(dstWidth > 0 && dstHeight > 0);
298     HI_ASSERT(!(dstWidth % HI_OVEN_BASE) && !(dstHeight % HI_OVEN_BASE));
299     int ret;
300 
301     /*
302      * 放大倍数
303      * magnification
304      */
305     double widthRate = ((double)dstWidth) / (double)srcWidth; // >1 means zoom in, <1 means zoom out
306     double heightRate = ((double)dstHeight) / (double)srcHeight; // >1 means zoom in, <1 means zoom out
307 
308     /*
309      * 根据缩放倍数分别处理
310      * Separate processing according to zoom factor
311      */
312     if (widthRate > rateMax || widthRate < rateMin ||
313         heightRate > rateMax || heightRate < rateMin) {
314         /*
315          * 缩放倍数超过一次VGS的最大值时,递归处理 ...
316          * When the zoom factor exceeds the maximum value of one VGS, recursive processing...
317          */
318         uint32_t midWidth = (uint32_t)IntZoomTo((int)srcWidth, widthRate, rateMin, rateMax);
319         uint32_t midHeight = (uint32_t)IntZoomTo((int)srcHeight, heightRate, rateMin, rateMax);
320         /*
321          * 确保为偶数。为奇数时,放大则减一,否则加一
322          *
323          * Make sure it is an even number. When it is an odd number,
324          * the zoom is reduced by one, otherwise it is increased by one
325          */
326         if (midWidth % HI_OVEN_BASE) {
327             midWidth += widthRate > 1 ? -1 : 1;
328         }
329         if (midHeight % HI_OVEN_BASE) {
330             midHeight += heightRate > 1 ? -1 : 1;
331         }
332 
333         SAMPLE_PRT("@@@ multi-lev vgs resize, src={%u, %u}, mid={%u, %u}, dst={%u, %u}, rate={%.4f, %.4f}\n",
334             srcWidth, srcHeight, midWidth, midHeight, dstWidth, dstHeight, widthRate, heightRate);
335 
336         /*
337          * 缩放一次
338          * Zoom once
339          */
340         VIDEO_FRAME_INFO_S midFrm;
341         ret = VgsResizeOnce(src, &midFrm, midWidth, midHeight);
342         if (ret != 0) {
343             SAMPLE_PRT("VgsResizeOnce(dw=%u, dh=%u) FAIL\n", midWidth, midHeight);
344             return ret;
345         }
346 
347         /*
348          * 以midFrm为src递归调用
349          * Recursively call with midFrm as src
350          */
351         ret = MppFrmResize(&midFrm, dst, dstWidth, dstHeight);
352         MppFrmDestroy(&midFrm);
353         if (ret != 0) {
354             SAMPLE_PRT("sub call MppFrmResize(dw=%u, dh=%u) FAIL\n", dstWidth, dstHeight);
355             return ret;
356         }
357     } else {
358         /*
359          * 缩放倍数未超过一次VGS的最大值,直接完成
360          * The zoom factor does not exceed the maximum value of VGS once, and it is done directly
361          */
362         ret = VgsResizeOnce(src, dst, dstWidth, dstHeight);
363         if (ret != 0) {
364             SAMPLE_PRT("VgsResizeOnce(dw=%u, dh=%u) FAIL\n", dstWidth, dstHeight);
365             return ret;
366         }
367     }
368     return ret;
369 }
370 
371 /*
372  * 将整数向上修整为偶数
373  * Round up integers to even numbers
374  */
IntToOven(int x)375 static inline int IntToOven(int x)
376 {
377     if (x % HI_OVEN_BASE == 0) {
378         return x;
379     } else {
380         return x + 1;
381     }
382 }
383 
384 /*
385  * 创建并执行VGS画线任务
386  * Create and execute VGS draw lines job
387  */
VgsDrawLines(VIDEO_FRAME_INFO_S * frm,const VGS_DRAW_LINE_S lines[],int lineNum)388 static HI_S32 VgsDrawLines(VIDEO_FRAME_INFO_S *frm, const VGS_DRAW_LINE_S lines[], int lineNum)
389 {
390     VGS_HANDLE jobHnd = -1;
391     VGS_TASK_ATTR_S task;
392     int ret;
393 
394     if (memset_s(&task, sizeof(task), 0, sizeof(task)) != EOK) {
395         HI_ASSERT(0);
396     }
397     task.stImgIn = *frm;
398     task.stImgOut = *frm;
399 
400     /*
401      * 启动一个job
402      * Start a job
403      */
404     ret = HI_MPI_VGS_BeginJob(&jobHnd);
405     if (ret != 0) {
406         SAMPLE_PRT("HI_MPI_VGS_BeginJob FAIL, ret=%08X\n", ret);
407         if (jobHnd >= 0 && HI_MPI_VGS_CancelJob(jobHnd) != HI_SUCCESS) {
408             HI_ASSERT(0);
409         }
410         return ret;
411     }
412     HI_ASSERT(jobHnd >= 0);
413 
414     /*
415      * 往一个已经启动的job里添加批量画线task
416      * Add a batch line drawing task to an already started job
417      */
418     ret = HI_MPI_VGS_AddDrawLineTaskArray(jobHnd, &task, lines, lineNum);
419     if (ret != 0) {
420         SAMPLE_PRT("HI_MPI_VGS_AddDrawLineTaskArray FAIL, ret=%08X\n", ret);
421         if (jobHnd >= 0 && HI_MPI_VGS_CancelJob(jobHnd) != HI_SUCCESS) {
422             HI_ASSERT(0);
423         }
424         return ret;
425     }
426 
427     /*
428      * 提交一个job
429      * Submit a job
430      */
431     ret = HI_MPI_VGS_EndJob(jobHnd);
432     if (ret != 0) {
433         SAMPLE_PRT("HI_MPI_VGS_EndJob FAIL, ret=%08X\n", ret);
434         if (jobHnd >= 0 && HI_MPI_VGS_CancelJob(jobHnd) != HI_SUCCESS) {
435             HI_ASSERT(0);
436         }
437         return ret;
438     }
439     return 0;
440 }
441 
442 /*
443  * 在frame中叠加一个或多个矩形框
444  * Superimpose one or more rectangular boxes in the frame
445  */
MppFrmDrawRects(VIDEO_FRAME_INFO_S * frm,const RectBox * boxes,int boxesNum,uint32_t color,int thick)446 int MppFrmDrawRects(VIDEO_FRAME_INFO_S *frm,
447     const RectBox *boxes, int boxesNum, uint32_t color, int thick)
448 {
449     VGS_DRAW_LINE_S lines[VGS_MAX_LINE];
450     int i;
451 
452     if (thick <= 0) {
453         HI_ASSERT(0);
454     }
455 
456     /*
457      * 将各矩形四边平面化为lines
458      * Planarize the four sides of each rectangle into lines
459      */
460     for (i = 0; i < boxesNum; i++) {
461         lines[RECT_LINES * i].stStartPoint.s32X = IntToOven(boxes[i].xmin);
462         lines[RECT_LINES * i].stStartPoint.s32Y = IntToOven(boxes[i].ymin);
463         lines[RECT_LINES * i].stEndPoint.s32X = IntToOven(boxes[i].xmax);
464         lines[RECT_LINES * i].stEndPoint.s32Y = IntToOven(boxes[i].ymin);
465         lines[RECT_LINES * i].u32Color = color;
466         lines[RECT_LINES * i].u32Thick = thick;
467         lines[RECT_LINES * i + RECT_LINE1].stStartPoint.s32X = IntToOven(boxes[i].xmax);
468         lines[RECT_LINES * i + RECT_LINE1].stStartPoint.s32Y = IntToOven(boxes[i].ymin);
469         lines[RECT_LINES * i + RECT_LINE1].stEndPoint.s32X = IntToOven(boxes[i].xmax);
470         lines[RECT_LINES * i + RECT_LINE1].stEndPoint.s32Y = IntToOven(boxes[i].ymax);
471         lines[RECT_LINES * i + RECT_LINE1].u32Color = color;
472         lines[RECT_LINES * i + RECT_LINE1].u32Thick = thick;
473         lines[RECT_LINES * i + RECT_LINE2].stStartPoint.s32X = IntToOven(boxes[i].xmax);
474         lines[RECT_LINES * i + RECT_LINE2].stStartPoint.s32Y = IntToOven(boxes[i].ymax);
475         lines[RECT_LINES * i + RECT_LINE2].stEndPoint.s32X = IntToOven(boxes[i].xmin);
476         lines[RECT_LINES * i + RECT_LINE2].stEndPoint.s32Y = IntToOven(boxes[i].ymax);
477         lines[RECT_LINES * i + RECT_LINE2].u32Color = color;
478         lines[RECT_LINES * i + RECT_LINE2].u32Thick = thick;
479         lines[RECT_LINES * i + RECT_LINE3].stStartPoint.s32X = IntToOven(boxes[i].xmin);
480         lines[RECT_LINES * i + RECT_LINE3].stStartPoint.s32Y = IntToOven(boxes[i].ymax);
481         lines[RECT_LINES * i + RECT_LINE3].stEndPoint.s32X = IntToOven(boxes[i].xmin);
482         lines[RECT_LINES * i + RECT_LINE3].stEndPoint.s32Y = IntToOven(boxes[i].ymin);
483         lines[RECT_LINES * i + RECT_LINE3].u32Color = color;
484         lines[RECT_LINES * i + RECT_LINE3].u32Thick = thick;
485     }
486     return VgsDrawLines(frm, lines, i * RECT_LINES);
487 }
488 
489 #ifdef __cplusplus
490 #if __cplusplus
491 }
492 #endif
493 #endif /* End of #ifdef __cplusplus */
494