其他分享
首页 > 其他分享> > 通过framebuffer显示png图片

通过framebuffer显示png图片

作者:互联网

一、前言

  png图片是无损压缩格式,它有一个比较重要的特点是可以让特定区域透明。要显示png图片需要调用png库留出的函数接口,png库留出的函数接口很多,但是帮助手册里没有详细的说明各个函数的用法,网上也没找到太多资料,因此在这里只介绍一种能用的方法。有些函数用法在代码注释里写着。

二、代码



  1 /**
  2  * filename: png.c
  3  * author: Suzkfly
  4  * date: 2021-08-14
  5  * platform: S3C2416或Ubuntu
  6  * 需要提前准备好lv.png这张图片,编译时要加-lpng参数,并且程序运行平台需要有png的库
  7  */
  8 #include <stdio.h>
  9 #include <stdlib.h>
 10 #include <string.h>
 11 #include <sys/stat.h>
 12 #include <sys/types.h>
 13 #include <unistd.h>
 14 #include <fcntl.h>
 15 #include <errno.h>
 16 #include <linux/fb.h>
 17 #include <sys/mman.h>
 18 #include <png.h>
 19 
 20 /**< \brief 定义每个像素点对应的位数,如果是565的屏则为16,如果是888的屏则为32 */
 21 #define BITS_PER_PIXEL    32
 22 //#define BITS_PER_PIXEL    16
 23 
 24 /**< \brief 根据实际情况修改,此处为unsigned short是565的屏,根据程序打印出的
 25     bits_per_pixel的值可以判断出输出格式是565还是888 */
 26 #if (BITS_PER_PIXEL == 32)
 27 typedef unsigned int color_t;
 28 #elif (BITS_PER_PIXEL == 16)
 29 typedef unsigned short color_t;
 30 #endif
 31 
 32 static struct fb_var_screeninfo __g_vinfo;    /* 显示信息 */
 33 color_t *__gp_frame;                    /* 虚拟屏幕首地址 */
 34 
 35 /* framebuffer初始化 */
 36 int framebuffer_init (void)
 37 {
 38     int fd = 0;
 39 
 40     fd = open("/dev/fb0", O_RDWR);
 41     if (fd == -1) {
 42         perror("fail to open /dev/fb0\n");
 43         return -1;
 44     }
 45 
 46     /* 获取显示信息 */
 47     ioctl(fd, FBIOGET_VSCREENINFO, &__g_vinfo);                     /* 获取显示信息 */
 48     printf("bits_per_pixel = %d\n", __g_vinfo.bits_per_pixel);      /* 得到一个像素点对应的位数 */
 49     printf("xres_virtual = %d\n", __g_vinfo.xres_virtual);            /* 打印虚拟屏幕列数 */
 50     printf("yres_virtual = %d\n", __g_vinfo.yres_virtual);            /* 打印虚拟屏幕行数 */
 51     printf("xres = %d\n", __g_vinfo.xres);                            /* 打印屏幕列数 */
 52     printf("yres = %d\n", __g_vinfo.yres);                            /* 打印屏幕行数 */
 53 
 54     /* 设置显示信息 */
 55     /*
 56     __g_vinfo.xres_virtual = 2048;
 57     __g_vinfo.yres_virtual = 1920;
 58     __g_vinfo.xres = 800;
 59     __g_vinfo.yres = 600;
 60     if (ioctl(fd, FBIOPUT_VSCREENINFO, &__g_vinfo)) {
 61         perror ("Get VScreen Info Error\n");
 62            exit (1);
 63     }
 64     */
 65 
 66     /* 获取显示信息 */
 67     //ioctl(fd, FBIOGET_VSCREENINFO, &__g_vinfo);                     /* 获取显示信息 */
 68     //printf("bits_per_pixel = %d\n", __g_vinfo.bits_per_pixel);      /* 得到一个像素点对应的位数 */
 69     //printf("xres_virtual = %d\n", __g_vinfo.xres_virtual);            /* 打印虚拟屏幕列数 */
 70     //printf("yres_virtual = %d\n", __g_vinfo.yres_virtual);            /* 打印虚拟屏幕行数 */
 71     //printf("xres = %d\n", __g_vinfo.xres);                            /* 打印屏幕列数 */
 72     //printf("yres = %d\n", __g_vinfo.yres);                            /* 打印屏幕行数 */
 73 
 74     __gp_frame = mmap(NULL,                             /* 映射区的开始地址,为NULL表示由系统决定映射区的起始地址 */
 75                       __g_vinfo.xres_virtual * __g_vinfo.yres_virtual * __g_vinfo.bits_per_pixel / 8,    /* 映射区大小 */
 76                       PROT_WRITE | PROT_READ,            /* 内存保护标志(可读可写) */
 77                       MAP_SHARED,                        /* 映射对象类型(与其他进程共享) */
 78                       fd,                                /* 有效的文件描述符 */
 79                       0);                                /* 被映射内容的偏移量 */
 80     if (__gp_frame == NULL) {
 81         perror("fail to mmap\n");
 82         return -1;
 83     }
 84 
 85     return 0;
 86 }
 87 
 88 /**
 89  * \brief 填充整屏
 90  */
 91 void full_screen (color_t color)
 92 {
 93     int i;
 94     color_t *p = __gp_frame;
 95 
 96     for (i = 0; i < __g_vinfo.xres_virtual * __g_vinfo.yres_virtual; i++) {
 97         *p++ = color;
 98     }
 99 }
100 
101 /**
102  * \brief 清屏
103  */
104 void clear()
105 {
106     full_screen(0);
107 }
108 
109 /**
110  * \brief 画点
111  */
112 void draw_point(int x, int y, color_t color)
113 {
114     color_t *p = __gp_frame;
115 
116     p += __g_vinfo.xres_virtual * y + x;
117     *p = color;
118 }
119 
120 /**
121  * \brief 在指定位置显示PNG图片
122  *
123  * \param[in] x:x坐标
124  * \param[in] y:y坐标
125  * \param[in] file_path:file_path图片文件路径
126  *
127  * \retval 成功返回0,失败返回-1
128  */
129 int Show_PNG(int x, int y, const char *file_path)
130 {
131     png_structp png_ptr;    /* 该结构可通过png_create_read_struct函数得到,作为内部结构,会作为参数传给其他函数,一般不会被用户使用 */
132     png_infop info_ptr;     /* 用于保存png图片的信息,直接访问容易导致程序出问题,因此如果想要获取png图片信息最好调用库提供的函数接口 */
133     int color_type = 0; 
134     int image_width, image_height;
135     char sig[8] = { 0 };    /* 8个字节用来存储png文件头 */
136     int i, j;
137     int R, G, B, A = 0;
138     FILE *p_fp;
139     png_bytep *row_pointers; /* 指针数组,数组中的每一个指针都指向一行图像数据 */
140     int bytes_per_pix = 0;   /* 一个像素点占用的字节数 */
141     color_t color;
142     
143     /* 打开png文件 */
144     if ((p_fp = fopen(file_path, "rb")) == NULL) {
145         fprintf(stderr, "%s open fail!!!\n", file_path);
146         return -1;
147     }
148     
149     /* 把文件前8个字节读出来,用来判断该文件是不是png文件,这8个字节必须是:
150         89 50 4e 47 0d 0a 1a 0a。如果使用了png_sig_cmp函数来判断文件头,那么后面
151         要读取数据之前要先调用png_set_sig_bytes,以告知文件头信息已经读取过了。
152         png_sig_cmp传递的第1个参数为需要检查的文件头数据指针,检查范围为
153         sig[第2个参数]~sig[第2个参数+第3个参数-1] */
154     fread(sig, 1, sizeof(sig), p_fp);
155     if (0 != png_sig_cmp(sig, 0, sizeof(sig))) {
156         fprintf(stderr, "The file is not PNG\n");
157         return -1;
158     }
159  
160     /* 创建一个png_structp结构体,第1个参数为png库的版本,直接传入
161        PNG_LIBPNG_VER_STRING宏即可,第2个参数为给出错处理函数(即第3个参数)传递
162        的指针,第3个参数为出错处理函数的指针,第4个参数为警告处理函数的指针。
163        如果后3个参数传NULL,则表示使用系统默认的处理函数 */
164     png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);    
165     if (png_ptr == NULL) {
166         fclose(p_fp);
167         return -1;
168     }
169  
170     /* 创建一个info_ptr,必须的 */    
171     info_ptr = png_create_info_struct(png_ptr);
172     if (info_ptr == NULL) {
173         fclose(p_fp);
174         png_destroy_read_struct(&png_ptr, NULL, NULL);
175         return -1;
176     }
177  
178     /* 如果上面png_create_read_struct没有自定义错误处理,这里是必须的 */
179     if (setjmp(png_jmpbuf(png_ptr))) {
180         png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
181         fclose(p_fp);
182  
183         return -1;
184     }
185  
186     /* 初始化文件IO,相当于将文件流指针和png_ptr绑定了 */
187     png_init_io(png_ptr, p_fp);
188     
189     /* 如果前面调用了png_sig_cmp,那么就要调用本函数,否则程序在运行时会报错 */
190     png_set_sig_bytes(png_ptr, sizeof(sig));
191  
192     /* 读取png图片信息。该函数相当于调用了png_read_info(),png_read_image()和
193        png_read_end()。如果调用了该函数,那么就不该再调用任何png_set_transform()
194        函数。
195        PNG_TRANSFORM_IDENTITY      No transformation
196        PNG_TRANSFORM_STRIP_16      Strip 16-bit samples to
197                                    8 bits
198        PNG_TRANSFORM_STRIP_ALPHA   Discard the alpha channel(丢弃透明度通道)
199        PNG_TRANSFORM_PACKING       Expand 1, 2 and 4-bit
200                                    samples to bytes
201        PNG_TRANSFORM_PACKSWAP      Change order of packed
202                                    pixels to LSB first
203        PNG_TRANSFORM_EXPAND        Perform set_expand()
204        PNG_TRANSFORM_INVERT_MONO   Invert monochrome images
205        PNG_TRANSFORM_SHIFT         Normalize pixels to the
206                                    sBIT depth
207        PNG_TRANSFORM_BGR           Flip RGB to BGR, RGBA
208                                    to BGRA
209        PNG_TRANSFORM_SWAP_ALPHA    Flip RGBA to ARGB or GA
210                                    to AG
211        PNG_TRANSFORM_INVERT_ALPHA  Change alpha from opacity
212                                    to transparency
213        PNG_TRANSFORM_SWAP_ENDIAN   Byte-swap 16-bit samples
214 
215        最后一个参数没有使用,传NULL即可*/
216     png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_EXPAND, NULL);
217     //png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_EXPAND | PNG_TRANSFORM_STRIP_ALPHA, NULL);
218  
219     if ((png_ptr != NULL) && (info_ptr != NULL)) {
220         /* 获取图像的宽高 */
221         image_width = png_get_image_width(png_ptr, info_ptr);       
222         image_height = png_get_image_height(png_ptr, info_ptr); 
223         printf("width:%d,height:%d\n", image_width, image_height);
224  
225         /* 获取图像颜色类型。如果在png_read_png中选择丢弃透明度通道,那么
226         color_type的值就是PNG_COLOR_TYPE_RGB,否则是PNG_COLOR_TYPE_RGB_ALPHA */
227         color_type = png_get_color_type(png_ptr, info_ptr); 
228         printf("color type:%d\n", color_type);
229     }
230  
231     /* 获取所有的图像数据 */
232     row_pointers = png_get_rows(png_ptr, info_ptr);
233     
234     /* RGB格式 */
235     if ((color_type == PNG_COLOR_TYPE_RGB) || (color_type == PNG_COLOR_TYPE_RGB_ALPHA)) {
236         if (color_type == PNG_COLOR_TYPE_RGB) {
237             bytes_per_pix = 3;
238         } else if (color_type == PNG_COLOR_TYPE_RGB_ALPHA) {
239             bytes_per_pix = 4;
240         }
241         
242         for(i = 0; i < image_height; i++) {
243             for(j = 0; j < (image_width * bytes_per_pix); j += bytes_per_pix) {
244                 R = row_pointers[i][j + 0];
245                 G = row_pointers[i][j + 1];
246                 B = row_pointers[i][j + 2];
247 #if (BITS_PER_PIXEL == 32)                 
248                 if (color_type == PNG_COLOR_TYPE_RGB_ALPHA) {
249                     A = row_pointers[i][j + 3];
250                 }
251                 /* 对于RGB888的显示屏来说,如果支持透明度,那么传入的颜色数据的高
252                    8位表示的就是透明度 */                
253                 color = (A << 24) | (R << 16) | (G << 8) | B;
254 #elif (BITS_PER_PIXEL == 16)
255                 R >>= 3;
256                 G >>= 2;
257                 B >>= 3;
258                 color = (R << 11) | (G << 5) | B;
259 #endif
260                 draw_point(j / bytes_per_pix + x, i + y, color);
261             }
262         }
263     }
264  
265     /* 清理图像,释放内存 */
266     png_destroy_read_struct(&png_ptr, &info_ptr, 0);
267     
268     fclose(p_fp);
269 
270     return 0;
271 }
272 
273 
274 int main(int argc, const char *argv[])
275 {
276     if (framebuffer_init()) {
277         return 0;
278     }
279 
280     /* 清屏 */
281     clear();
282 
283     Show_PNG(100, 100, "lv.png");
284 
285     return 0;
286 }

说明:lv.png这张图片需要提前准备好,编译时要加-lpng参数。

三、问题和总结

  1. 如果程序运行时报错:

  error while loading shared libraries: libpng.so.3: cannot open shared object file: No such file or directory

  说明运行程序的平台上缺少png库,需要去下载,并将下载好的库复制到/lib目录下。

  我下载的库在这里:libpng12.tar.gz

  2. 如果显示的图片有透明区域,那么图片显示出来会有一些黑色区域(不一定是框框,反正就是大片的黑色区域),如下图(右),左边是我在电脑上打开后显示的样子。

  

  为了解决这个问题,我在网上搜了很多资料,发现确实有很多网友遇到过这样的问题,但他们都是用别的平台或者语言。在linux下有很多显示png图片的资料,但是却没有人提到过这个问题。

  有意思的是,我将这张图片通过微信发出去,在聊天窗口中显示的也是带黑框的(同上图右相同),但是将图片点开,并点击查看原图,黑框就会消失。这至少说明这是一个普遍存在并且可以解决的问题,希望以后的某一天我也能找到解决办法。

标签:__,color,vinfo,png,framebuffer,ptr,PNG,图片
来源: https://www.cnblogs.com/Suzkfly/p/15142201.html