自定义bmp图像缩放及在lcd显示屏任意位置显示

在LCD上任意位置显示一张任意大小并且宽高变为原来1/n大小的色深为 24bit的bmp图片

头文件

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <linux/fb.h>
#include <strings.h>

从此处开始以1字节对齐

#pragma pack(1) // 结构体以1字节对齐

自定义BMP文件头部结构,方便后续获取或创建bmp图片使用

typedef struct
{
    unsigned short bfType;
    unsigned int bfSize;
    unsigned short bfReserved1;
    unsigned short bfReserved2;
    unsigned int bfOffBits;
} BITMAPFILEHEADER;

typedef struct
{
    unsigned int biSize;
    int biWidth;  // 宽
    int biHeight; // 高
    unsigned short biPlanes;
    unsigned short biBitCount; // 色深
    unsigned int biCompression;
    unsigned int biSizeImage;
    int biXPelsPerMeter;
    int biYPelsPerMeter;
    unsigned int biClrUsed;
    unsigned int biClrImportant;
} BITMAPINFOHEADER;

在此处取消字节对齐

#pragma pack()

调用此函数,可以将bmp图片正常缩放2及4的倍数倍(未进行字节对齐操作,后续补齐)
传入的参数分别通过命令行及运行时的标准输入传入,

/*******************************************************************
 *
 *	函数名称:	Scalinbmp
 *	函数功能:   在LCD上任意位置显示一张任意大小并且宽高变为原来1/n大小的色深为 24bit的bmp图片
 *	函数参数:
 *				@name 	bmp图像文件名
 *				@x		图像显示的起点x轴坐标
 *				@y		图像显示的起点y轴左边
 *				@n		想要缩放的倍数
 *	返回结果:
 * 	注意事项:   None
 * 	函数作者:   mailLinL@163.com
 *	创建日期:   2024/05/14
 *	修改历史:
 *	函数版本:	V1.0
 * *****************************************************************/
int Scalinbmp(char *name, int x, int y, int n)
{

    // 1.打开bmp文件,获取文件头信息,图像大小,图像宽,高,位深度等可用信息
    FILE *bmp_src = fopen(name, "rb");
    if (NULL == bmp_src)
    {
        printf("open SRCFILE is error!\n");
        return -1;
    }
    BITMAPFILEHEADER src_head;
    BITMAPINFOHEADER src_vinfo;
    fread(&src_head, 1, 14, bmp_src);  // 读取bmp图像的文件头段	获取文件大小 以字节为单位
    fread(&src_vinfo, 1, 40, bmp_src); // 读取bmp图像的信息头段	获取文件宽,高 以像素点为单位 位深度以bits为单位
    int width = src_vinfo.biWidth;
    int height = src_vinfo.biHeight;
    // 3.以wb权限打开新建bmp图片,并向新的bmp图像的文件头中录入数据
    FILE *bmp_new = fopen("new.bmp", "wb");
    src_head.bfSize = src_vinfo.biWidth / n * src_vinfo.biHeight / n * src_vinfo.biBitCount / 8 + 54;
    // 新bmp文件的总大小 = 源bmp像素点宽的一半 * 源像素点高的一半 * bmp图像位深度 / 比特 + 新文件头54字节
    src_vinfo.biWidth = src_vinfo.biWidth / n;
    // 新bmp文件的像素点宽 = 源bmp像素点宽的一半
    src_vinfo.biHeight = src_vinfo.biHeight / n;
    // 新bmp文件的图像区大小 = 源bmp图像区大小的一半
    // src_vinfo.biSizeImage = src_vinfo.biSizeImage / 4;
    fwrite(&src_head, 1, 14, bmp_new);
    fwrite(&src_vinfo, 1, 40, bmp_new);

    // 创建缓冲区,每次读取一行存入缓冲区
    char *line_size = (char *)calloc(1, width * 3);
    // 循环将源bmp图片的颜色分量输入到新bmp文件中
    for (int i = 0; i < height * 3; i += n)
    {                                            // 外层循环,隔行读取
        fread(line_size, 1, width * 3, bmp_src); // 每次读取一行,存入缓冲区
        for (int j = 0; j < width * 3; j += 3 * n)
        {
            fwrite(&line_size[j], 3, 1, bmp_new);
        }
        bzero(line_size, width * 3);
        fseek(bmp_src, width * 3 * (n - 1), SEEK_CUR);
    }
    /*    // 5.打开lcd并建立lcd映射内存
        int lcd_fd = open("/dev/fb0", O_RDWR);
        if (lcd_fd == -1)
        {
            printf("mmap for lcd is error\n");
            return -1;
        }
        // 调用LCD屏的像素 获取屏幕的宽高信息
        struct fb_var_screeninfo lcd_vinfo;
        ioctl(lcd_fd, FBIOGET_VSCREENINFO, &lcd_vinfo);
        int *lcd_mp = (int *)mmap(NULL,                                // 申请内存映射的地址,填NULL让MMU自行分配
                                  lcd_vinfo.xres * lcd_vinfo.yres * 4, // 申请的空间大小,以lcd屏实际像素大小*每个像素点的字节数
                                  PROT_READ | PROT_WRITE,              // 映射空间的操作权限,读 |写
                                  MAP_SHARED,                          // 映射空间对其他成员的权限 共享
                                  lcd_fd,                              // lcd文件指示符
                                  0);                                  // 映射空间起始偏移量
        // 5.将新的bmp图像位置指示器设置在颜色分量数据起始地址
        fseek(bmp_new, 54, SEEK_SET);

        // 6.定义缓冲区,获取新bmp图像的颜色分量,行列都缩放为原来的一半,像素宽/2*像素高/2乘以位深度/8
        char new_buff[src_vinfo.biHeight * src_vinfo.biWidth * src_vinfo.biBitCount / 8];
        // 初始化缓冲区
        bzero(new_buff, src_vinfo.biWidth * src_vinfo.biHeight * src_vinfo.biBitCount / 8);
        //				    像素点宽		       像素点高的				位深度
        // 7.将新的bmp图像中的颜色分量写入lcd映射内存中
        int data = 0;
        int cnt = 0;
        for (int i = (y + src_vinfo.biHeight - 1); i >= y; i--)
        { // bmp图像写入lcd的数据录入方式是自底向上,外层循环以指定位置到向上偏移bmp图像高度单位为终点
            for (int j = y; j < (src_vinfo.biWidth + y); j++)
            { // 内层循环写入lcd的数据录入方式是自左向右,以指定位置到向后偏移bmp图像宽度单位为止
                data |= new_buff[cnt];
                data |= new_buff[cnt + 1] << 8;
                data |= new_buff[cnt + 2] << 16;
                lcd_mp[i * (lcd_vinfo.xres) + j] = data;
                cnt += 3;
                data = 0;
            }
        }
    */
    // 8.关闭源bmp图片,新bmp图片,lcd屏,释放映射内存
    fclose(bmp_src);
    fclose(bmp_new);
    // close(lcd_fd);
    // munmap(new_buff, lcd_vinfo.xres * lcd_vinfo.yres * 4);

    return 0;
}

主函数中测试

int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        printf("argument is error!\n");
        return -1;
    }
    if (NULL == argv[1])
    {
        printf("argument 2 is error!\n");
        return -1;
    }
    int x, y, n;
    scanf("%d%d%d", &x, &y, &n);
    Scalinbmp(argv[1], x, y, n);

    return 0;
}

源bmp图片

image

文件属性

image

运行程序后生成的新bmp图片

image

文件属性

image

注:此代码仍需优化,考虑字节对齐,优化后将及时更改

posted @   林大官人995  阅读(111)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架
点击右上角即可分享
微信分享提示