This is an automated email from the ASF dual-hosted git repository. xiaoxiang781216 pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx-apps.git
commit b017038e5fc2b6506df8346c076a7625a8046187 Author: fangyibo <[email protected]> AuthorDate: Wed Sep 16 21:21:36 2026 +0800 nxcamera: scale and centre preview on all display paths ConvertToARGB only crops and never scales, so the preview wrote the capture at full size into the panel: a frame larger than the panel overflowed the framebuffer and a smaller one was not centred. The MJPEG path was worse, since passing the panel size as crop size made libyuv's MJpegDecoder reject the frame. Convert every source to full-size ARGB first, then ARGBScale it to fit the panel keeping the aspect ratio, centre it with black bars, and emit RGB24, RGB565 or ARGB according to the panel format. Treat every non-zero libyuv result as a failure (decode errors return positive 1, not a negative errno), and clear the panel once at open time so a scaled-down frame does not keep stale pixels around it. Signed-off-by: fangyibo <[email protected]> --- include/system/nxcamera.h | 2 + system/nxcamera/nxcamera.c | 277 +++++++++++++++++++++++---------------------- 2 files changed, 146 insertions(+), 133 deletions(-) diff --git a/include/system/nxcamera.h b/include/system/nxcamera.h index 4f5fa318a..b1a9ba040 100644 --- a/include/system/nxcamera.h +++ b/include/system/nxcamera.h @@ -76,6 +76,8 @@ struct nxcamera_s #ifdef CONFIG_LIBYUV FAR uint8_t *conv_buf; /* Preallocated ARGB conv buffer */ size_t conv_buf_size; /* Size of conv buffer in bytes */ + FAR uint8_t *scale_buf; /* Preallocated scaled ARGB buffer */ + size_t scale_buf_size; /* Size of scale buffer in bytes */ #endif }; diff --git a/system/nxcamera/nxcamera.c b/system/nxcamera/nxcamera.c index 96c4879be..d491b4645 100644 --- a/system/nxcamera/nxcamera.c +++ b/system/nxcamera/nxcamera.c @@ -62,9 +62,10 @@ #ifdef CONFIG_LIBYUV # define CONVERT_TO_ARGB ConvertToARGB -# define CONVERT_FROM_I420 ConvertFromI420 -# define CONVERT_TO_I420 ConvertToI420 # define CONVERT_ARGB_TO_RGB24 ARGBToRGB24 +# define CONVERT_ARGB_TO_RGB565 ARGBToRGB565 +# define SCALE_ARGB ARGBScale +# define FILTER_BILINEAR kFilterBilinear #endif #define NXCAMERA_STATE_IDLE 0 @@ -84,6 +85,7 @@ static void pan_display(int fb_device, FAR struct fb_planeinfo_s *plane_info) { struct pollfd pfd; int ret; + pfd.fd = fb_device; pfd.events = POLLOUT; @@ -98,158 +100,164 @@ static void pan_display(int fb_device, FAR struct fb_planeinfo_s *plane_info) static int show_image(FAR struct nxcamera_s *pcam, FAR v4l2_buffer_t *buf) { #ifdef CONFIG_LIBYUV - /* Use bytesused for the actual payload size. For compressed formats - * such as JPEG, bytesused is much smaller than the buffer capacity. + /* Use bytesused for actual data size. For compressed formats (JPEG), + * bytesused is the actual payload size, which is typically much smaller + * than buf_sizes (the buffer capacity). This is the correct value to + * pass to libyuv convert function as sample_size. */ uint32_t sample_size = buf->bytesused > 0 ? buf->bytesused : pcam->buf_sizes[buf->index]; - if (pcam->display_vinfo.fmt == FB_FMT_RGB32) + FAR uint8_t *fbmem = (FAR uint8_t *)pcam->display_pinfo.fbmem; + int src_w = pcam->fmt.fmt.pix.width; + int src_h = pcam->fmt.fmt.pix.height; + int panel_w = pcam->display_vinfo.xres; + int panel_h = pcam->display_vinfo.yres; + int dst_w; + int dst_h; + int x_off; + int y_off; + size_t need_convert; + size_t need_scale; + int ret; + + if (src_w <= 0 || src_h <= 0 || panel_w <= 0 || panel_h <= 0) { - return CONVERT_TO_ARGB(pcam->bufs[buf->index], - sample_size, - pcam->display_pinfo.fbmem, - pcam->display_pinfo.stride, - 0, - 0, - pcam->fmt.fmt.pix.width, - pcam->fmt.fmt.pix.height, - pcam->fmt.fmt.pix.width, - pcam->fmt.fmt.pix.height, - 0, - pcam->fmt.fmt.pix.pixelformat); + return -EINVAL; } - else if (pcam->display_vinfo.fmt == FB_FMT_RGB16_565) - { - if (pcam->fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_YUV420) - { - return CONVERT_FROM_I420(pcam->bufs[buf->index], - pcam->fmt.fmt.pix.width, - &pcam->bufs[buf->index][ - pcam->fmt.fmt.pix.width * - pcam->fmt.fmt.pix.height], - pcam->fmt.fmt.pix.width / 2, - &pcam->bufs[buf->index][ - pcam->fmt.fmt.pix.width * - pcam->fmt.fmt.pix.height * 5 / 4], - pcam->fmt.fmt.pix.width / 2, - pcam->display_pinfo.fbmem, - pcam->display_pinfo.stride, - pcam->fmt.fmt.pix.width, - pcam->fmt.fmt.pix.height, - V4L2_PIX_FMT_RGB565); - } - else - { - int ret; - FAR uint8_t *dst = malloc(pcam->fmt.fmt.pix.width * - pcam->fmt.fmt.pix.height * 3 / 2); - if (!dst) - { - return -ENOMEM; - } - ret = CONVERT_TO_I420(pcam->bufs[buf->index], - sample_size, - dst, - pcam->fmt.fmt.pix.width, - &dst[pcam->fmt.fmt.pix.width * - pcam->fmt.fmt.pix.height], - pcam->fmt.fmt.pix.width / 2, - &dst[pcam->fmt.fmt.pix.width * - pcam->fmt.fmt.pix.height * 5 / 4], - pcam->fmt.fmt.pix.width / 2, - 0, - 0, - pcam->fmt.fmt.pix.width, - pcam->fmt.fmt.pix.height, - pcam->fmt.fmt.pix.width, - pcam->fmt.fmt.pix.height, - 0, - pcam->fmt.fmt.pix.pixelformat); - if (ret < 0) - { - goto err; - } + /* Scale the source down/up to fit the panel keeping the aspect ratio, + * then centre it with black bars on the under-utilised sides. + */ - ret = CONVERT_FROM_I420(dst, - pcam->fmt.fmt.pix.width, - &dst[pcam->fmt.fmt.pix.width * - pcam->fmt.fmt.pix.height], - pcam->fmt.fmt.pix.width / 2, - &dst[pcam->fmt.fmt.pix.width * - pcam->fmt.fmt.pix.height * 5 / 4], - pcam->fmt.fmt.pix.width / 2, - pcam->display_pinfo.fbmem, - pcam->display_pinfo.stride, - pcam->fmt.fmt.pix.width, - pcam->fmt.fmt.pix.height, - V4L2_PIX_FMT_RGB565); - if (ret < 0) - { - goto err; - } + if (src_w * panel_h > panel_w * src_h) + { + dst_w = panel_w; + dst_h = src_h * panel_w / src_w; + } + else + { + dst_h = panel_h; + dst_w = src_w * panel_h / src_h; + } -err: - free(dst); - return ret; - } + x_off = (panel_w - dst_w) / 2; + y_off = (panel_h - dst_h) / 2; + + if (dst_w == 0 || dst_h == 0) + { + return -EINVAL; } - else if (pcam->display_vinfo.fmt == FB_FMT_RGB24) + + /* Lazy-allocate conversion buffers, reused across frames. conv_buf + * holds the full-size ARGB conversion of the source frame, scale_buf + * the scaled result (not needed when the panel itself is RGB32, which + * consumes the scaled ARGB directly). + */ + + need_convert = (size_t)src_w * src_h * 4; + need_scale = (size_t)dst_w * dst_h * 4; + + if (pcam->conv_buf_size < need_convert) { - int ret; - uint32_t dst_w = pcam->display_vinfo.xres; - uint32_t dst_h = pcam->display_vinfo.yres; - uint32_t src_w = pcam->fmt.fmt.pix.width; - uint32_t src_h = pcam->fmt.fmt.pix.height; - int crop_x = (src_w > dst_w) ? (src_w - dst_w) / 2 : 0; - int crop_y = (src_h > dst_h) ? (src_h - dst_h) / 2 : 0; - size_t need = dst_w * dst_h * 4; - - /* Lazy-allocate conversion buffer, reused across frames */ - - if (pcam->conv_buf_size < need) + free(pcam->conv_buf); + pcam->conv_buf = malloc(need_convert); + if (pcam->conv_buf == NULL) { - free(pcam->conv_buf); - pcam->conv_buf = malloc(need); - if (pcam->conv_buf == NULL) - { - pcam->conv_buf_size = 0; - return -ENOMEM; - } - - pcam->conv_buf_size = need; + pcam->conv_buf_size = 0; + return -ENOMEM; } - ret = CONVERT_TO_ARGB(pcam->bufs[buf->index], - sample_size, - pcam->conv_buf, - dst_w * 4, - crop_x, - crop_y, - src_w, - src_h, - dst_w, - dst_h, - 0, - pcam->fmt.fmt.pix.pixelformat); - if (ret < 0) + pcam->conv_buf_size = need_convert; + } + + if (pcam->display_vinfo.fmt != FB_FMT_RGB32 && + pcam->scale_buf_size < need_scale) + { + free(pcam->scale_buf); + pcam->scale_buf = malloc(need_scale); + if (pcam->scale_buf == NULL) { - return ret; + pcam->scale_buf_size = 0; + return -ENOMEM; } - return CONVERT_ARGB_TO_RGB24(pcam->conv_buf, - dst_w * 4, - pcam->display_pinfo.fbmem, - pcam->display_pinfo.stride, - dst_w, - dst_h); + pcam->scale_buf_size = need_scale; } - return 0; + /* Convert the whole source frame into ARGB first. Keeping crop_width + * at the capture width satisfies libyuv's MJpegDecoder, which requires + * crop_width to equal the JPEG width. + */ + + ret = CONVERT_TO_ARGB(pcam->bufs[buf->index], + sample_size, + pcam->conv_buf, + src_w * 4, + 0, + 0, + src_w, + src_h, + src_w, + src_h, + 0, + pcam->fmt.fmt.pix.pixelformat); + if (ret != 0) + { + verr("ConvertToARGB failed: %d for %dx%d\n", + ret, src_w, src_h); + return -EINVAL; + } + + switch (pcam->display_vinfo.fmt) + { + case FB_FMT_RGB32: + return SCALE_ARGB(pcam->conv_buf, src_w * 4, src_w, src_h, + fbmem + y_off * pcam->display_pinfo.stride + + x_off * 4, + pcam->display_pinfo.stride, + dst_w, dst_h, FILTER_BILINEAR); + + case FB_FMT_RGB24: + ret = SCALE_ARGB(pcam->conv_buf, src_w * 4, src_w, src_h, + pcam->scale_buf, dst_w * 4, + dst_w, dst_h, FILTER_BILINEAR); + if (ret != 0) + { + return -EINVAL; + } + + return CONVERT_ARGB_TO_RGB24(pcam->scale_buf, dst_w * 4, + fbmem + + y_off * pcam->display_pinfo.stride + + x_off * 3, + pcam->display_pinfo.stride, + dst_w, dst_h); + + case FB_FMT_RGB16_565: + ret = SCALE_ARGB(pcam->conv_buf, src_w * 4, src_w, src_h, + pcam->scale_buf, dst_w * 4, + dst_w, dst_h, FILTER_BILINEAR); + if (ret != 0) + { + return -EINVAL; + } + + return CONVERT_ARGB_TO_RGB565(pcam->scale_buf, dst_w * 4, + fbmem + + y_off * pcam->display_pinfo.stride + + x_off * 2, + pcam->display_pinfo.stride, + dst_w, dst_h); + + default: + return -ENOTSUP; + } #else - FAR uint32_t *pbuf = (FAR uint32_t *)pcam->bufs[buf->index]; + FAR uint32_t *pbuf; + + pbuf = (FAR uint32_t *)pcam->bufs[buf->index]; vinfo("show image from %p: %" PRIx32 " %" PRIx32, pbuf, pbuf[0], pbuf[1]); return 0; #endif @@ -319,6 +327,8 @@ static int nxcamera_opendevice(FAR struct nxcamera_s *pcam) return -errcode; } + memset(pcam->display_pinfo.fbmem, 0, pcam->display_pinfo.fblen); + return OK; } else @@ -1116,6 +1126,7 @@ void nxcamera_release(FAR struct nxcamera_s *pcam) { #ifdef CONFIG_LIBYUV free(pcam->conv_buf); + free(pcam->scale_buf); #endif pthread_mutex_destroy(&pcam->mutex); free(pcam);
