自定義bmp影像縮放及在lcd螢幕任意位置顯示

林大官人995發表於2024-05-14

在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

注:此程式碼仍需最佳化,考慮位元組對齊,最佳化後將及時更改

相關文章