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);

Reply via email to