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