lcd4linux实现输出framebuffer图像到LCD

根据文章: Linux下支持AX206 LCD FOR AIDA64 Business ,可以实现输出png图片到AX206 LCD。那么如果我们想输出framebuffer的图像到LCD屏幕呢?如何实现?

根据git@github.com:ukoda/lcd4linux-ax206.git仓库的代码,我们可以看到png图片的渲染是通过widget_image.c中的 static void widget_image_render(const char *Name, WIDGET_IMAGE * Image) 函数实现的。

我们可以通过如下修改,实现输出framebuffer图像到AX206 LCD的效果:

diff --git a/contrib/picoLCD/dpf_480_320.conf b/contrib/picoLCD/dpf_480_320.conf
new file mode 100644
index 0000000..34e6b51
--- /dev/null
+++ b/contrib/picoLCD/dpf_480_320.conf
@@ -0,0 +1,42 @@
+Display dpf {
+    Driver     'DPF'
+    Port       'usb0'
+    Font       '6x8'
+    Foreground 'ffffff'
+    Background '000000'
+    Basecolor  '000000'
+    Orientation 0          # 0 = landscape, 1 = portrait
+                          # 2 = reverse landscape, 3 = reverse portrait
+    Backlight  backlight   # Backlight variable control, 0..7 (0=off, 7=max)
+}
+
+Widget Bgnd {
+    class 'Image'
+    file '/dev/fb1'
+    reload 1
+    update 1000
+    inverted 0
+    visible 1
+       fb_width 480
+       fb_height 320
+       fb_bpp 32
+}
+
+Display 'DPF'
+
+Layout layout_480x320 {
+       Layer 2 {
+               X1.Y1 'Bgnd'
+    }
+}
+
+Layout 'layout_480x320'
+
+Variables {
+    backlight 7
+    tick 500
+    tack 200
+    minute 60000
+}
+
+
diff --git a/widget_image.c b/widget_image.c
index accabfe..2238731 100644
--- a/widget_image.c
+++ b/widget_image.c
@@ -38,6 +38,10 @@
 #include <string.h>
 #include <ctype.h>
 #include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
 
 #ifdef HAVE_GD_GD_H
 #include <gd/gd.h>
@@ -69,6 +73,111 @@
 #include <dmalloc.h>
 #endif
 
+static int obtain_frame_from_framebuffer(unsigned char *frame_buf,
+       char *fb_dev, int width, int height, int bpp)
+{
+       int fb_fd = open(fb_dev, O_RDWR);
+       if (fb_fd < 0) {
+               error("open dev %s failed", fb_dev);
+               return -1;
+       }
+
+       int nread = read(fb_fd, frame_buf, width*height*bpp);
+       if (nread <= 0) {
+               error("read failed from framebuffer:%d", nread);
+               close(fb_fd);
+               return -1;
+       }
+
+       close(fb_fd);
+       return 0;
+}
+
+static gdImagePtr createImageFromRgb32(unsigned char *rgbData, int width, int height)
+{
+       gdImagePtr im;
+       int x, y;
+       int color;
+
+       im = gdImageCreateTrueColor(width, height);
+       if (!im) {
+               error("Could not create image.\n");
+               return NULL;
+       }
+
+       for (y = 0; y < height; y++) {
+               for (x = 0; x < width; x++) {
+                       int index = (y * width + x) * 4;
+
+                       unsigned char b = rgbData[index];
+                       unsigned char g = rgbData[index + 1];
+                       unsigned char r = rgbData[index + 2];
+
+                       color = gdImageColorAllocate(im, r, g, b);
+
+                       gdImageSetPixel(im, x, y, color);
+               }
+       }
+
+       return im;
+}
+
+static gdImagePtr createImageFromRgb24(unsigned char *rgbData, int width, int height)
+{
+       gdImagePtr im;
+       int x, y;
+       int color;
+
+       im = gdImageCreateTrueColor(width, height);
+       if (!im) {
+               error("Could not create image.\n");
+               return NULL;
+       }
+
+       for (y = 0; y < height; y++) {
+               for (x = 0; x < width; x++) {
+                       int index = (y * width + x) * 3;
+
+                       unsigned char r = rgbData[index];
+                       unsigned char g = rgbData[index + 1];
+                       unsigned char b = rgbData[index + 2];
+
+                       color = gdImageColorAllocate(im, r, g, b);
+
+                       gdImageSetPixel(im, x, y, color);
+               }
+       }
+
+       return im;
+}
+
+static int create_image_from_framebuffer(char *dev_fb, WIDGET_IMAGE *Image)
+{
+       int width, height, Bpp;
+       int retval;
+
+       width = P2N(&Image->fb_width);
+       height = P2N(&Image->fb_height);
+       Bpp = P2N(&Image->fb_bpp) / 8;
+       unsigned char img_buf[width*height*Bpp];
+
+       /* we only support rgba and rgb current */
+       if (width <= 0 || height <= 0 || (Bpp != 4 && Bpp != 3))
+               return -1;
+
+       retval = obtain_frame_from_framebuffer(img_buf, dev_fb, width, height, Bpp);
+       if (retval)
+               return -1;
+
+       if (Bpp == 4)
+               Image->gdImage = createImageFromRgb32(img_buf, width, height);
+       else if (Bpp == 3)
+               Image->gdImage = createImageFromRgb24(img_buf, width, height);
+       else
+               return -1;
+
+       return 0;
+}
 
 static void widget_image_render(const char *Name, WIDGET_IMAGE * Image)
 {
@@ -105,13 +214,22 @@ static void widget_image_render(const char *Name, WIDGET_IMAGE * Image)
             return;
         }
 
-        fd = fopen(file, "rb");
-        if (fd == NULL) {
-            error("Warning: Image %s: fopen(%s) failed: %s", Name, file, strerror(errno));
-            return;
-        }
-        Image->gdImage = gdImageCreateFromPng(fd);
-        fclose(fd);
+       char *f_ptr = strstr(file, "/dev/fb");
+       if (f_ptr) {
+               int retval = create_image_from_framebuffer(file, Image);
+               if (retval) {
+                       error("Warning: get frame from %s failed", file);
+                       return;
+               }
+       } else {
+               fd = fopen(file, "rb");
+               if (fd == NULL) {
+                       error("Warning: Image %s: fopen(%s) failed: %s", Name, file, strerror(errno));
+                       return;
+               }
+               Image->gdImage = gdImageCreateFromPng(fd);
+               fclose(fd);
+       }
 
         if (Image->gdImage == NULL) {
             fd = fopen(file, "rb");
@@ -293,6 +411,9 @@ static void widget_image_update(void *Self)
         property_eval(&Image->visible);
         property_eval(&Image->inverted);
         property_eval(&Image->center);
+       property_eval(&Image->fb_width);
+       property_eval(&Image->fb_height);
+       property_eval(&Image->fb_bpp);
 
         /* render image into bitmap */
         widget_image_render(W->name, Image);
@@ -343,6 +464,9 @@ int widget_image_init(WIDGET * Self)
         property_load(section, "visible", "1", &Image->visible);
         property_load(section, "inverted", "0", &Image->inverted);
         property_load(section, "center", "0", &Image->center);
+       property_load(section, "fb_width", "480", &Image->fb_width);
+       property_load(section, "fb_height", "320", &Image->fb_height);
+       property_load(section, "fb_bpp", "32", &Image->fb_bpp);
 
         /* sanity checks */
         if (!property_valid(&Image->file)) {
@@ -389,6 +513,10 @@ int widget_image_quit(WIDGET * Self)
                 property_free(&Image->visible);
                 property_free(&Image->inverted);
                 property_free(&Image->center);
+               property_free(&Image->fb_width);
+               property_free(&Image->fb_height);
+               property_free(&Image->fb_bpp);
+
                 free(Self->data);
                 Self->data = NULL;
             }
diff --git a/widget_image.h b/widget_image.h
index 6ead2b4..4d64a9a 100644
--- a/widget_image.h
+++ b/widget_image.h
@@ -46,6 +46,9 @@ typedef struct WIDGET_IMAGE {
     PROPERTY visible;           /* image visible? */
     PROPERTY inverted;          /* image inverted? */
     PROPERTY center;            /* image centered? */
+    PROPERTY fb_width;         /* size of the framebuffer width */
+    PROPERTY fb_height;                /* size of the framebuffer height */
+    PROPERTY fb_bpp;           /* bits per pixel */
 } WIDGET_IMAGE;
 
 extern WIDGET_CLASS Widget_Image;

如上述修改所示,我们可以通过配置文件dpf_480_320.conf里Bgnd widget的文件名参数 file 来指定要使用哪个framebuffer设备。想要使用fb0就配置file为“/dev/fb0”, 想使用fb1就配置为"/dev/fb1"。这样可以兼容输出png图片到LCD的功能。

配置文件解析:

  1. dpf_480_320.conf里Bgnd widget的fb_width和fb_height分别表示AX206 LCD分辨率的宽和高。
  2. dpf_480_320.conf里Bgnd widget的fb_bpp参数根据你系统内framebuffer的bpp(bits per pixel)来决定。
    • 如果framebuffer的bpp为32(RGBA), 则fb_bpp参数配置为32。如果framebuffer的bpp为24(RGB888),则fb_bpp参数配置为24。

framebuffer的bpp可以使用fbset工具来设置,具体使用方法如下:

fbset -fb /dev/fb1 -g 480 320 480 320 32

上面的命令是设置fb1的bpp为32, width为480,height为320。

我们可以通过dpf_480_320.conf里Bgnd widget的update参数调整LCD刷新的间隔,单位为ms。

上述 obtain_frame_from_framebuffer 函数实现从framebuffer里获取RGB raw数据。

由于GD库没有API直接实现把RGB raw数据生成gdImage的功能,我这里写了两个函数 createImageFromRgb32 和 createImageFromRgb24 来实现。

经过上述对lcd4linux源码的改动,我们已经可以输出framebuffer图像到AX206 LCD了。

 

posted @ 2024-03-28 18:23  闹闹爸爸  阅读(92)  评论(0编辑  收藏  举报