• 用LCD显示BMP(位图)


    基本概念

     BMP是英文Bitmap的缩写,由称作像素(图片元素)的单个点组成的,每个像素点由三个字节(用char型定义)组成,按照蓝绿红排列。这些点可以进行不同的排列和染色以构成图样。如下图所示,当读取图片信息时,文件指针由左下角开始增长。如下图所示,BMP图片包含了14个字节的文件头信息,和40和字节的BMP图片信息,读取BMP数据的时候注意主要跳过。

     下图为LCD显示屏,它的数据从上角开始增长的,而且是由四个字节(用int型定义)组成一个像素点,而且原色排列也与BMP排序不同,编程时注意。

     下图为BMP转换为LCD显示的过程。

    使用LCD显示位图

     该程序的文件结构如下:

     需要注意的是,BMP图片大小应该与LCD分辨率一致,不然将会错位。
    main.c

    #include "main.h"
    
    int main(int argc, char const *argv[])
    {
        lcd_info  lcd; 
    
        BITMAPFILEHEADER file_head; //存放文件头的结构体
        BITMAPINFOHEADER bmp_info;  //存放bmp数据的结构体
    
        int bmp_size;
        int x;
        int y;
        int color;
    
        char bmp_buf[LCD_W*LCD_H*3]; //存放bmp数据
    
        lcd = init_lcd(LCD_PATH);
        
        int fd_bmp = init_bmp(BMP_PATH);
    
        file_head =  read_file_head(fd_bmp, file_head);   //获得文件头,并且移动文件指针
        bmp_info  =  read_file_bmp_info(fd_bmp, bmp_info); //获得bmp图片信息,并且将文件指针移动到了bmp图片的数据部分
    
        int ret = read(fd_bmp, &bmp_buf, LCD_W*LCD_H*3); //获得文件数据,并且将用char类型来存放
    
        if (-1 == ret)
        {
            printf("read bmp_data msg: %s
    ", strerror(errno));
        }
        printf("read bmp_data %d
    ", ret);
    
        for (y = 0; y < LCD_H; y++)
        {
            for ( x = 0; x < LCD_W; x++)
            {
             /* 将BMP的3个元素组成一个能在LCD上正确显示的像素 */
             color = bmp_buf[(x+y*800)*3+0] << 0 |            
                     bmp_buf[(x+y*800)*3+1] << 8 |
                     bmp_buf[(x+y*800)*3+2] << 16;
             draw_point(lcd.p_lcd, color, x, LCD_H - 1 - y); //每获得一个像素就根据坐标去打印它,注意纵轴方向需要倒着打印,因为LCD与位图的显示与存放的方式不同
            }
        }
        return 0;
    }
    

    lcd.c

    #include "lcd.h"
    #include "main.h"
    
    lcd_info init_lcd(const char *path)
    {
        lcd_info lcd = {
            .fd_lcd = -1,
            .p_lcd  = NULL
        };
       
         lcd.fd_lcd = open(LCD_PATH , O_RDWR);
        if (-1 ==  lcd.fd_lcd)
        {
            printf("open  lcd.fd_lcd msg: %s
    ", strerror(errno));
            return lcd;
        }
        lcd.p_lcd = mmap(NULL, LCD_SIZE,  PROT_WRITE | PROT_READ , MAP_SHARED,  lcd.fd_lcd , 0);
        if (MAP_FAILED == lcd.p_lcd)
        {
            printf("mmap msg: %s
    ", strerror(errno));
            return lcd;
        }
    
        return lcd;
    }
    /* 画点函数
     **/
    bool draw_point(int *address, int color, int x, int y)
    {
        if (NULL == address)
        {
            printf("draw_point msg:%s
    ", strerror(errno));  
            return false;
        }
    
        *(address + (x + (y*800))) = color; 
    
        return true;
    }
    
    

    bmp.c

    #include "bmp.h"
    
    /* 初始化图片获得图片句柄
     **/
    int init_bmp(const char *bmp_path)
    {
        int fd_bmp = open(bmp_path, O_RDONLY);
        if (-1 == fd_bmp)
        {
            printf("open fd_bmp msg: %s
    ", strerror(errno));
            return -1;
        }
        return fd_bmp;
    }
    
    BITMAPFILEHEADER read_file_head(int fd_bmp, BITMAPFILEHEADER file_head)
    {
        int ret = read(fd_bmp, &file_head, sizeof(BITMAPFILEHEADER));
        if (-1 == ret)
        {
            printf("read file_head msg: %s
    ", strerror(errno));
            return file_head;
        }
        printf("read file_head %d
    ", ret);
    
        return file_head;
    }
    
    BITMAPINFOHEADER read_file_bmp_info(int fd_bmp, BITMAPINFOHEADER bmp_info)
    {
        int ret = read(fd_bmp, &bmp_info, sizeof(BITMAPINFOHEADER));
        if (-1 == ret)
        {
            printf("read bmp_info msg: %s
    ", strerror(errno));
            return bmp_info;
        }
        printf("read bmp_info %d
    ", ret);
    
        return bmp_info;
    }
    

    main.h

    #define __MAIN__H__
    
    #include <stdlib.h>
    
    #include "lcd.h"
    #include "bmp.h"
    
    #define     LCD_PATH    "/dev/fb0"
    #define     BMP_PATH    "./1.bmp" 
    
    #define     LCD_W       800
    #define     LCD_H       480
    
    #define     LCD_SIZE    LCD_W*LCD_H*4
    
    #endif
    

    lcd.h

    #ifndef __LCD__H__
    #define __LCD__H__
    
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <string.h>
    #include <stdbool.h>
    #include <errno.h>
    #include <sys/mman.h>
    
    typedef struct Lcd_Info{
    
        int  fd_lcd;
        int* p_lcd;
    }lcd_info;
    
    lcd_info init_lcd(const char *path);
    
    bool draw_point(int *address, int color, int x, int y);
    
    #endif
    

    bmp.h

    #ifndef __BMP__H__
    #define __BMP__H__
    
    #include "lcd.h"
    #include "main.h"
    
    typedef    short            WORD;
    typedef    int              DWORD;
    typedef    long             LONG;
    
    typedef struct tagBITMAPFILEHEADER
    {
        WORD bfType;//位图文件的类型,必须为BM(1-2字节)
        DWORD bfSize;//位图文件的大小,以字节为单位(3-6字节,低位在前)
        WORD bfReserved1;//位图文件保留字,必须为0(7-8字节)
        WORD bfReserved2;//位图文件保留字,必须为0(9-10字节)
        DWORD bfOffBits;//位图数据的起始位置,以相对于位图(11-14字节,低位在前)
        //文件头的偏移量表示,以字节为单位
    }__attribute__((packed)) BITMAPFILEHEADER;
    
    typedef struct tagBITMAPINFOHEADER{
        DWORD biSize;//本结构所占用字节数(15-18字节)
        LONG biWidth;//位图的宽度,以像素为单位(19-22字节)
        LONG biHeight;//位图的高度,以像素为单位(23-26字节)
        WORD biPlanes;//目标设备的级别,必须为1(27-28字节)
        WORD biBitCount;//每个像素所需的位数,必须是1(双色),(29-30字节)
        //4(16色),8(256色)16(高彩色)或24(真彩色)之一
        DWORD biCompression;//位图压缩类型,必须是0(不压缩),(31-34字节)
        //1(BI_RLE8压缩类型)或2(BI_RLE4压缩类型)之一
        DWORD biSizeImage;//位图的大小(其中包含了为了补齐行数是4的倍数而添加的空字节),以字节为单位(35-38字节)
        LONG biXPelsPerMeter;//位图水平分辨率,每米像素数(39-42字节)
        LONG biYPelsPerMeter;//位图垂直分辨率,每米像素数(43-46字节)
        DWORD biClrUsed;//位图实际使用的颜色表中的颜色数(47-50字节)
        DWORD biClrImportant;//位图显示过程中重要的颜色数(51-54字节)
    }__attribute__((packed)) BITMAPINFOHEADER;
    
    int init_bmp(const char *bmp_path);
    
    BITMAPFILEHEADER read_file_head(int fd_bmp, BITMAPFILEHEADER file_head);
    
    BITMAPINFOHEADER read_file_bmp_info(int fd_bmp, BITMAPINFOHEADER bmp_info);
    
    #endif
    

    Makefile

    CC=arm-linux-gcc
    TAG=./bin/main
    SRC=$(wildcard ./src/*.c)
    objs = ./src/main.o ./src/lcd.o ./src/bmp.o
    override CONFIG += -I./inc 
    
    $(TAG):$(SRC)
    	$(CC) $^ -o $@ $(CONFIG) 
    $(SRC):$(OBJ)
    	$(CC) $^ -o $@ -c $(objs)
    

    使用LCD显示小图

     上个程序只能够显示固定大小的图片,因为LCD是顺序存放的,当图片大小和分辨不一样将会错位,如当LCD的一行像素足够存放BMP像素数据的两行,LCD便会将需要分两行显示的数据,显示成一行,从而导致了数据错位。这里只需要更改主程序,其他程序是一样的。
    main.c

    #include "main.h"
     
    
    int main(int argc, char const *argv[])
    {
        lcd_info  lcd; 
    
        BITMAPFILEHEADER file_head;
        BITMAPINFOHEADER bmp_info;
    
        int bmp_size;
        int x;
        int y;
        int color; 
    
        lcd = init_lcd(LCD_PATH);
        
        int fd_bmp = init_bmp(BMP_PATH);
    
        file_head =  read_file_head(fd_bmp, file_head);   
        bmp_info  =  read_file_bmp_info(fd_bmp, bmp_info);
    
        int bmp_w = bmp_info.biWidth; //获得图片宽度,循环时用到
        int bmp_h = bmp_info.biHeight;//获得图片高度,循环时用到
    
        char bmp_buf[LCD_W*LCD_H*3];
        int ret = read(fd_bmp, &bmp_buf, LCD_W*LCD_H*3);
    
        if (-1 == ret)
        {
            printf("read bmp_data msg: %s
    ", strerror(errno));
        }
        printf("read bmp_data %d
    ", ret);
    
                // 输出文件的信息
        printf("type:%x	size:%d	offset:%d
    " , file_head.bfType , file_head.bfSize,file_head.bfOffBits );
        printf("biWidth:%ld	biHeight:%ld	biBitCount:%d	biSizeImage:%d
    ",
                bmp_info.biWidth,
                bmp_info.biHeight,
                bmp_info.biBitCount,
                bmp_info.biSizeImage);
    
        int tmp_y = 0;
    
        //bmp每行像素的所占字节数需要被4整除,但不满足这个条件时需要补充字节
        int swallow = 0 ; //定义需要补充的字节数变量
        if ((swallow = ((bmp_w*3)%4)) != 0 ) //bmp_w*3求出bmp每行所占的字节数,再取余,得到余数
        {
            swallow = 4 - swallow; //向上补充
            printf("需要补充%d个空字节!!
    " , swallow );
        }
        else 
        {
            printf("不需要补充空字节!!
    "  );
            swallow = 0 ;
        }
               
        for (y = 0; y < bmp_h; y++)
        {
            for ( x = 0; x < bmp_w; x++)
            {
                /*  y*swallow:表示y每增加1需要跳过的字节数,因为是补充字节是没有数据的,而且不跳过会导致LCD显示错位 */
                color = bmp_buf[(x + y * bmp_w ) * 3 + 0 + y * swallow] << 0 |
                        bmp_buf[(x + y * bmp_w ) * 3 + 1 + y * swallow] << 8 |
                        bmp_buf[(x + y * bmp_w ) * 3 + 2 + y * swallow] << 16;
    
                
                tmp_y = (y * bmp_w + y * (LCD_W-bmp_w)) / LCD_W;  //这里是将BMP的纵轴坐标转换LCD纵轴坐标。横坐标不需要管,因为不会错位。
    
                draw_point(lcd.p_lcd, color, x, bmp_h - 1 - tmp_y);
            
            }
        }
        return 0;
    }
    
    
  • 相关阅读:
    左偏树
    论在Windows下远程连接Ubuntu
    ZOJ 3711 Give Me Your Hand
    SGU 495. Kids and Prizes
    POJ 2151 Check the difficulty of problems
    CodeForces 148D. Bag of mice
    HDU 3631 Shortest Path
    HDU 1869 六度分离
    HDU 2544 最短路
    HDU 3584 Cube
  • 原文地址:https://www.cnblogs.com/ding-ding-light/p/14170827.html
Copyright © 2020-2023  润新知