首页 > 其他分享 >娱乐小平板(功能:相册、游戏、画图、刮刮乐)

娱乐小平板(功能:相册、游戏、画图、刮刮乐)

时间:2024-09-11 21:23:45浏览次数:12  
标签:picture 相册 int 画图 ts bmp pos && 刮乐

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/mman.h> //mmap
#include <linux/input.h>
#include <string.h>
#include <pthread.h>
#include <stdlib.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;

#define LCD_WIDTH 800
#define LCD_HEIGHT 480
#define RGB(r, g, b) ((r << 16) | (g << 8) | b)        // 定义颜色宏
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0])) // 数组长度

char color_buf[800 * 480 * 4] = {0}; // 颜色缓存器

unsigned int *g_fb = NULL; // 指向显存的首地址
int g_fd_lcd;              // 显存文件描述符
int g_fd_ts;               // 触摸屏文件描述符

/*菜单结构体*/
typedef struct _menu_t
{
    const char *name;       // 菜单的名字
    int (*fun)(void);       // 菜单执行的功能函数
    struct _menu_t *parent; // 父菜单
    struct _menu_t *child;  // 子菜单
} menu_t;

// 触摸屏坐标结构体
typedef struct _ts_pos_t
{
    int x;
    int y;
    int sta;
} ts_pos_t;

// 描点操作
void lcd_draw_point(int x, int y, unsigned int color)
{
    *(g_fb + y * 800 + x) = color;
}

// 清屏操作
void lcd_clear(unsigned int color)
{
    int x, y;
    for (y = 479; y >= 0; y--)
    {
        for (x = 0; x < 800; x++)
        {
            // 对每个像素点进行赋值
            lcd_draw_point(x, y, color);
        }
    }
}

// 填充函数
void lcd_fill(int x, int y, int width, int height, unsigned int color)
{
    int x_s = x;
    int y_s = y;
    int x_e = x + width;
    int y_e = y + height;

    if (x_e > LCD_WIDTH) // 防止越界
        x_e = LCD_WIDTH;

    if (y_e > LCD_HEIGHT)
        y_e = LCD_HEIGHT;

    for (y = y_s; y < y_e; y++)
    {
        for (x = x_s; x < x_e; x++)
        {
            // 对每个像素点进行赋值
            lcd_draw_point(x, y, color);
        }
    }
}

// 位图显示
int lcd_draw_bmp(const char *pathname, int x, int y)
{

    // 打开 bmp
    int fd_bmp = open(pathname, O_RDONLY);

    if (fd_bmp < 0)
    {
        printf("open %s fail\n", pathname);
        return -1;
    }

    // 位图文件头
    BITMAPFILEHEADER file_head;
    read(fd_bmp, &file_head, sizeof(file_head));
    printf("%s图片大小:%d bytes\n", pathname, file_head.bfSize);

    // 位图信息段,获取位图的高度、宽度、颜色深度
    BITMAPINFOHEADER info_head;
    read(fd_bmp, &info_head, sizeof(info_head));
    printf("%s图片尺寸:宽%ld 高%ld\n", pathname, info_head.biWidth, info_head.biHeight);
    printf("%s图片颜色深度:%d\n", pathname, info_head.biBitCount);

    int bmp_rgb_size = info_head.biWidth * info_head.biHeight * info_head.biBitCount / 8;

    // 定义一个变长数组
    char bmp_buf[bmp_rgb_size];

    // 读取该bmp的所有RGB的数据
    read(fd_bmp, bmp_buf, bmp_rgb_size);

    // 关闭bmp文件
    close(fd_bmp);

    // 开始显示图片
    int x_s = x;
    int y_s = y;
    int x_e = x_s + info_head.biWidth;
    int y_e = y_s + info_head.biHeight;

    int x_pos, y_pos;

    unsigned int color;
    int i = 0;

    for (y_pos = y_e - 1; y_pos >= y_s; y_pos--)
    {
        for (x_pos = x_s; x_pos < x_e; x_pos++, i += 3)
        {

            color = (bmp_buf[i + 2] << 16) | (bmp_buf[i + 1] << 8) | bmp_buf[i];
            lcd_draw_point(x_pos, y_pos, color);
        }
    }

    return 0;
}

int lcd_draw_game(const char *pathname, int x, int y)
{

    // 打开 bmp
    int fd_bmp = open(pathname, O_RDONLY);
    if (fd_bmp < 0)
    {
        printf("open %s fail\n", pathname);
        return -1;
    }

    // 位图文件头
    BITMAPFILEHEADER file_head;
    read(fd_bmp, &file_head, sizeof(file_head));
    printf("%s图片大小:%d bytes\n", pathname, file_head.bfSize);

    // 位图信息段,获取位图的高度、宽度、颜色深度
    BITMAPINFOHEADER info_head;
    read(fd_bmp, &info_head, sizeof(info_head));
    printf("%s图片尺寸:宽%ld 高%ld\n", pathname, info_head.biWidth, info_head.biHeight);
    printf("%s图片颜色深度:%d\n", pathname, info_head.biBitCount);

    int bmp_rgb_size = info_head.biWidth * info_head.biHeight * info_head.biBitCount / 8;

    // 定义一个变长数组
    char bmp_buf[bmp_rgb_size];

    // 读取该bmp的所有RGB的数据
    read(fd_bmp, bmp_buf, bmp_rgb_size);

    // 关闭bmp文件
    close(fd_bmp);

    // 开始显示图片
    int x_s = x;
    int y_s = y;
    int x_e = x_s + info_head.biWidth;
    int y_e = y_s + info_head.biHeight;

    int x_pos, y_pos;

    unsigned int color;
    int i = 0;

    for (y_pos = y_e - 1; y_pos >= y_s; y_pos--)
    {
        for (x_pos = x_s; x_pos < x_e; x_pos++, i += 3)
        {
            color = (bmp_buf[i + 2] << 16) | (bmp_buf[i + 1] << 8) | bmp_buf[i];

            if (color == 0xFFFFFF)
                continue;
            lcd_draw_point(x_pos, y_pos, color);
        }
    }
    return 0;
}

// 打开显存
int lcd_open(const char *pathname)
{
    // 打开/dev/fb0
    int g_fd_lcd = open(pathname, O_RDWR);

    if (g_fd_lcd < 0)
    {
        printf("open %s fail\n", pathname);
        return -1;
    }

    // 将设备文件/dev/fb0映射到内存
    g_fb = mmap(NULL,                   // 映射区的起始地址由系统自动分配
                800 * 480 * 4,          // 映射内存的大小,往往填写文件的大小
                PROT_READ | PROT_WRITE, // 映射区的保护形式,当前是可读可写
                MAP_SHARED,             // 共享,把映射内存的数据同步到文件
                g_fd_lcd,               // 映射的文件描述符
                0);                     // 文件的偏移量,0就不需要进行的偏移

    if (g_fb == MAP_FAILED)
    {
        printf("mmap fail\n");
        return -1;
    }

    return 0;
}

int lcd_close(void)
{
    if (g_fd_lcd > 0)
        close(g_fd_lcd);

    // 解除内存映射
    if (g_fb)
        munmap(g_fb, 800 * 480 * 4);

    return 0;
}

int ts_open(const char *pathname)
{
    // 打开触摸屏设备
    g_fd_ts = open(pathname, O_RDONLY);

    if (g_fd_ts < 0)
    {
        perror("open");
        return -1;
    }

    return 0;
}

int ts_close(void)
{
    if (g_fd_ts > 0)
        close(g_fd_ts);

    return 0;
}

int ts_read(int *x, int *y, int *sta)
{
    if (g_fd_ts < 0)
        return -1;

    struct input_event ie;

    while (1)
    {
        read(g_fd_ts, &ie, sizeof(ie));

        // 绝对坐标的事件
        if (ie.type == EV_ABS)
        {
            if (ie.code == ABS_X)
                *x = ie.value * 800 / 1024; // 黑色边框的触摸屏需要*800/1024

            if (ie.code == ABS_Y)
                *y = ie.value * 480 / 600; // 黑色边框的触摸屏需要*800/1024
        }
        if (ie.type == EV_KEY && ie.code == BTN_TOUCH)
        {
            *sta = ie.value;
            break;
        }
    }
    return 0;
}

menu_t menu_startup;
menu_t menu_login;
menu_t menu_main;

int menu_startup_func(void) // 开机界面
{
    int x = 0, y = 0;
    int ts_x, ts_y, ts_sta;
    int ts_x_press, ts_y_press;
    int rt;

    // 显示开机界面
    lcd_draw_bmp("picture/startup.bmp", 0, 0);
    sleep(1);

    // 显示开车蹦迪进度条
    int bmp_width = 140;
    int bmp_height = 140;

    y = LCD_HEIGHT - bmp_height;
    int i = 0;

    const char *bmp_tbl[] = {
        "picture/boot_animation/1.bmp",
        "picture/boot_animation/2.bmp",
        "picture/boot_animation/3.bmp",
        "picture/boot_animation/4.bmp",
        "picture/boot_animation/5.bmp",
        "picture/boot_animation/6.bmp",
        "picture/boot_animation/7.bmp",
        "picture/boot_animation/8.bmp",
        "picture/boot_animation/9.bmp",
        "picture/boot_animation/10.bmp",

    };

    while (1)
    {
        rt = ts_read(&ts_x, &ts_y, &ts_sta);
        if (rt < 0)
            continue;

        printf("上滑登录");

        if (ts_sta)
        {
            ts_x_press = ts_x;
            ts_y_press = ts_y;
            continue;
        }
        else if (ts_y_press - ts_y > 80)
        {
            printf("向上滑动\n");
            break;
        }
    }

    while (x + bmp_width < LCD_WIDTH)
    {
        // 填充背景色,清空残留的图像
        lcd_fill(0, y, x + bmp_width, bmp_height, RGB(246, 244, 249));

        // 显示开车蹦迪
        lcd_draw_bmp(bmp_tbl[i % 10], x, y);

        // 图片往前移动30个像素点
        x += 30;

        // 延时50ms
        usleep(50 * 1000);

        // 指定下一张开车蹦迪的图片
        i++;
    }
    return 0;
}

int menu_login_func(void) // 登录界面
{
    int ts_x, ts_y, ts_sta;
    int rt;

    char pass_correct[6] = {'1', '2', '3', '4', '5', '6'};
    char pass_input[7] = {0};
    char pass_cnt = 0;

    lcd_draw_bmp("picture/login.bmp", 0, 0);

    while (1)
    {
        rt = ts_read(&ts_x, &ts_y, &ts_sta);

        if (rt < 0)
            continue;

        printf("ts_x:%d ts_y:%d ts_sta:%d\n", ts_x, ts_y, ts_sta);

        // 若是按压状态,则重新新的一轮循化
        if (ts_sta)
            continue;

        if (ts_x > 295 && ts_x < 375 && ts_y > 345 && ts_y < 460) // 隐藏入口
            break;

        if (ts_x >= 175 && ts_x <= 255) // 最右列:369Y
        {
            if (ts_y >= 285 && ts_y <= 365) // Y
            {
                if (memcmp(pass_correct, pass_input, 6) == 0)
                {
                    lcd_draw_bmp("picture/yes.bmp", 300, 200);
                    sleep(1);
                    printf("密码正确\n");
                    break;
                }
                lcd_draw_bmp("picture/no.bmp", 300, 200);
                sleep(1);
                printf("密码错误,请重新输入...\n");
                lcd_draw_bmp("picture/login.bmp", 0, 0);

                pass_cnt = 0;
                memset(pass_input, 0, sizeof(pass_input));

                continue;
            }
            if (ts_y >= 205 && ts_y <= 285) // 9
            {
                pass_input[pass_cnt] = '9';
                if (pass_cnt < 5)
                    pass_cnt++;
            }

            if (ts_y >= 125 && ts_y <= 205) // 6
            {
                pass_input[pass_cnt] = '6';
                if (pass_cnt < 5)
                    pass_cnt++;
            }

            if (ts_y >= 50 && ts_y <= 125) // 3
            {
                pass_input[pass_cnt] = '3';
                if (pass_cnt < 5)
                    pass_cnt++;
            }

            printf("当前输入的密码:%s\n", pass_input);
        }

        if (ts_x >= 85 && ts_x <= 175) // 中间列:2580
        {
            if (ts_y >= 285 && ts_y <= 365) // 0
            {
                pass_input[pass_cnt] = '0';
                if (pass_cnt < 5)
                    pass_cnt++;
            }

            if (ts_y >= 205 && ts_y <= 285) // 8
            {
                pass_input[pass_cnt] = '8';
                if (pass_cnt < 5)
                    pass_cnt++;
            }

            if (ts_y >= 125 && ts_y <= 205) // 5
            {
                pass_input[pass_cnt] = '5';
                if (pass_cnt < 5)
                    pass_cnt++;
            }

            if (ts_y >= 50 && ts_y <= 125) // 2
            {
                pass_input[pass_cnt] = '2';
                if (pass_cnt < 5)
                    pass_cnt++;
            }

            printf("当前输入的密码:%s\n", pass_input);
        }

        if (ts_x >= 0 && ts_x <= 85) // 最左列:147N
        {
            if (ts_y >= 285 && ts_y <= 365) // n
            {
                pass_cnt = 0;

                memset(pass_input, 0, sizeof(pass_input));
                printf("已清空输入密码,请重新输入...\n");
            }

            if (ts_y >= 205 && ts_y <= 285) // 7
            {
                pass_input[pass_cnt] = '7';
                if (pass_cnt < 5)
                    pass_cnt++;
            }

            if (ts_y >= 125 && ts_y <= 205) // 4           
            {
                pass_input[pass_cnt] = '4';
                if (pass_cnt < 5)
                    pass_cnt++;
            }

            if (ts_y >= 50 && ts_y <= 125) // 1
            {
                pass_input[pass_cnt] = '1';
                if (pass_cnt < 5)
                    pass_cnt++;
            }
            printf("当前输入的密码:%s\n", pass_input);
        }

        if (ts_x >= 720 && ts_x <= 800 && ts_y >= 0 && ts_y <= 80) // 返回开机界面
        {
            printf("返回开机界面\n");
            return 1;
        }
    }
    return 0;
}

int icon_app1_func(void);
int icon_app2_func(void);
int icon_app3_func(void);
int icon_app4_func(void);

int menu_main_func(void) // 主界面
{
    int ts_x, ts_y, ts_sta;
    int icon_pos = 0;
    int rt;

    menu_t menu_icon_tbl[] = {
        {.name = "主界面", .fun = NULL, .parent = NULL, .child = NULL},
        {.name = "相册", .fun = icon_app1_func, .parent = &menu_main, .child = NULL},
        {.name = "游戏", .fun = icon_app2_func, .parent = &menu_main, .child = NULL},
        {.name = "绘画", .fun = icon_app3_func, .parent = &menu_main, .child = NULL},
        {.name = "彩票", .fun = icon_app4_func, .parent = &menu_main, .child = NULL}};

    lcd_draw_bmp("picture/main.bmp", 0, 0);
    while (1)
    {
        rt = ts_read(&ts_x, &ts_y, &ts_sta);

        if (rt < 0)
            continue;

        printf("ts_x:%d ts_y:%d ts_sta:%d\n", ts_x, ts_y, ts_sta);

        // 若是按压状态,则重新新的一轮循环
        if (ts_sta)
            continue;

        if (ts_x >= 725 && ts_x <= 800 && ts_y >= 0 && ts_y <= 70)
        {
            /* 返回1,表示进入父菜单*/
            return 1;
        }

        if (ts_x >= 0 && ts_x <= 90)
        {
            if (ts_y >= 0 && ts_y <= 90)
            {
                icon_pos = 1;
            }
            else if (ts_y >= 90 && ts_y <= 175)
            {
                icon_pos = 2;
            }
            else if (ts_y >= 175 && ts_y <= 275)
            {
                icon_pos = 3;
            }
            else if (ts_y >= 275 && ts_y <= 370)
            {
                icon_pos = 4;
            }

            else
            {
                continue;
            }

            printf("运行%s\n", menu_icon_tbl[icon_pos].name);

            if (menu_icon_tbl[icon_pos].fun)
                menu_icon_tbl[icon_pos].fun();

            icon_pos = 0;
            lcd_draw_bmp("picture/main.bmp", 0, 0);
        }
    }

    return 0;
}

void *thread_game_func(void *parg) // 游戏线程
{
    ts_pos_t *ts_pos = (ts_pos_t *)parg;

    lcd_draw_bmp("picture/game_animation/game.bmp", 0, 0);

    const char *bmp_game_pet[] = {
        "picture/game_animation/pet/1.bmp",
        "picture/game_animation/pet/2.bmp",
        "picture/game_animation/pet/3.bmp",
        "picture/game_animation/pet/4.bmp",
        "picture/game_animation/pet/5.bmp",
        "picture/game_animation/pet/6.bmp",
        "picture/game_animation/pet/7.bmp",
        "picture/game_animation/pet/8.bmp",
    };

    printf("thread_game_func is created success\n");

    int i = 0;
    int x = 660, y = 330;
    int b = 0;
    int energy = 100; // 能量值

    while (1)
    {
        // 显示游戏背景图
        lcd_draw_bmp("picture/game_animation/game.bmp", 0, 0);

        // 显示宠物小精灵
        lcd_draw_game(bmp_game_pet[i % 8], x, y);

        // 点击电灯图标补充能量值
        if (ts_pos->x > 0 && ts_pos->x < 75 && ts_pos->y > 80 && ts_pos->y < 175)
        {
            printf("energy full\n"); // 满能量

            energy = 100;

            // 清空读取到的坐标值、按压状态
            bzero(ts_pos, sizeof(ts_pos_t));
        }

        switch (energy) // 不同能量值显示不同颜色
        {
        case 90 ... 100:
            lcd_fill(80, 10, 1 + energy, 30, RGB(0, 255, 0)); // 绿色
            break;

        case 60 ... 89:
            lcd_fill(80, 10, 1 + energy, 30, RGB(255, 255, 0)); // 黄色
            break;

        default:
            lcd_fill(80, 10, 1 + energy, 30, RGB(255, 0, 0)); // 红色
            break;
        }

        if (energy >= 5) // 每次扣5点血
            energy -= 5;

        i++;

        if (b)
        {
            if (x + 100 < LCD_WIDTH)
                x += 10; // 宠物向右移动10像素点
            else
                b = !b;
        }
        else
        {
            if (x >= 10) // 宠物向左移动10像素点
                x -= 10;
            else
                b = !b;
        }
        usleep(500 * 1000);
    }
}

int icon_app1_func(void) // 相册
{
    const char *bmp_photo_tbl[] = {
        "picture/photo_album/1.bmp",
        "picture/photo_album/2.bmp",
        "picture/photo_album/3.bmp",
        "picture/photo_album/4.bmp",
        "picture/photo_album/5.bmp",
        "picture/photo_album/6.bmp",
        "picture/photo_album/7.bmp",
        "picture/photo_album/8.bmp",
        "picture/photo_album/9.bmp",
        "picture/photo_album/10.bmp",
        "picture/photo_album/11.bmp",
        "picture/photo_album/12.bmp",
        "picture/photo_album/13.bmp",
        "picture/photo_album/14.bmp",
        "picture/photo_album/15.bmp",
        "picture/photo_album/16.bmp",
        "picture/photo_album/17.bmp",
        "picture/photo_album/18.bmp",
        "picture/photo_album/19.bmp",
        "picture/photo_album/20.bmp",
        "picture/photo_album/21.bmp",
        "picture/photo_album/22.bmp",
        "picture/photo_album/23.bmp",
        "picture/photo_album/24.bmp",
        "picture/photo_album/25.bmp",
        "picture/photo_album/26.bmp",
        "picture/photo_album/27.bmp",
        "picture/photo_album/28.bmp",
    };

    printf("icon_app1_func\n");

    int i = 0;
    int ts_x_press, ts_y_press;
    int ts_x, ts_y, ts_sta;
    int rt;

    lcd_draw_bmp(bmp_photo_tbl[0], 0, 0);

    while (1)
    {
        rt = ts_read(&ts_x, &ts_y, &ts_sta);

        if (rt < 0)
            continue;

        printf("[app1] ts_x:%d ts_y:%d ts_sta:%d\n", ts_x, ts_y, ts_sta);

        // 保存点击后的坐标
        if (ts_sta)
        {
            ts_x_press = ts_x;
            ts_y_press = ts_y;
            continue;
        }
        else
        {
            // 左滑切换上一张图片
            if (ts_x - ts_x_press < -90)
            {
                i--;
                if (i < 0)
                    i = 27;
                lcd_draw_bmp(bmp_photo_tbl[i], 0, 0);

                printf("向左滑动\n");
            }

            // 右滑切换下一张图片
            if (ts_x - ts_x_press > 90)
            {
                i++;
                if (i > 27)
                    i = 0;
                lcd_draw_bmp(bmp_photo_tbl[i], 0, 0);
                printf("向右滑动\n");
            }

            // 下滑退出
            if (ts_y - ts_y_press > 150)
            {
                printf("向下滑动\n");
                break;
            }
        }
    }
    return 1;
}

int icon_app2_func(void) // 游戏
{
    const char *bmp_game_tbl[] = {
        "picture/game_animation/1.bmp",
        "picture/game_animation/2.bmp",
        "picture/game_animation/3.bmp",
        "picture/game_animation/4.bmp",
        "picture/game_animation/5.bmp",
        "picture/game_animation/6.bmp",
        "picture/game_animation/7.bmp",
        "picture/game_animation/8.bmp",
        "picture/game_animation/9.bmp",
        "picture/game_animation/10.bmp",
        "picture/game_animation/11.bmp",
    };

    printf("icon_app2_func\n");

    int i;
    int ts_x, ts_y, ts_sta;
    int rt;

    // 显示游戏进入动画
    for (i = 0; i < ARRAY_SIZE(bmp_game_tbl); i++)
    {
        lcd_draw_bmp(bmp_game_tbl[i], 0, 0);
        usleep(30 * 1000);
    }

    pthread_t tid;

    ts_pos_t ts_pos;

    // 创建游戏线程,并传递ts_pos参数给线程
    pthread_create(&tid, NULL, thread_game_func, &ts_pos);

    while (1)
    {
        // 获取坐标值
        rt = ts_read(&ts_x, &ts_y, &ts_sta);

        if (rt < 0)
            continue;

        if (ts_sta)
            continue;

        printf("[icon_func1_func] ts_x:%d ts_y:%d ts_sta:%d\n", ts_x, ts_y, ts_sta);

        // 设置ts_pos成员,由于其ts_pos变量地址已经传递给线程,线程可以访问到
        ts_pos.x = ts_x;
        ts_pos.y = ts_y;
        ts_pos.sta = ts_sta;

        // 点击右上角,游戏退出
        if (ts_x > 635 && ts_x < 780 && ts_y > 15 && ts_y < 60)
        {
            // 退出游戏线程
            pthread_cancel(tid);
            break;
        }
    }
    return 1;
}

void lcd_draw_circle(unsigned r_x, unsigned int r_y, unsigned int r, unsigned int color)
{
    unsigned int x, y;
    if (r_x - r < 0)
    {
        r_x = r;
    }
    if (r_y - r < 0)
    {
        r_y = r;
    }
    for (y = r_y - r; y < r_y + r; y++)
    {
        for (x = r_x - r; x < r_x + r; x++)
        {
            if ((r_x - x) * (r_x - x) + (r_y - y) * (r_y - y) <= r * r) // 判断是否大于圆的范围
            {
                lcd_draw_point(x, y, color);
            }
        }
    }
}

int icon_app3_func(void) // 绘画
{
    int ts_x, ts_y, ts_sta;
    int ts_x_press, ts_y_press;
    unsigned int pen_color = RGB(0, 0, 0);
    int rt;
    int r = 10;

    printf("icon_app3_func\n");

    lcd_draw_bmp("picture/painting.bmp", 0, 0);

    struct input_event ie;

    while (1)
    {
        rt = read(g_fd_ts, &ie, sizeof(ie));
        if (rt < 0)
            continue;

        // 绝对坐标值的事件
        if (ie.type == EV_ABS)
        {
            if (ie.code == ABS_X)
                ts_x = ie.value * 800 / 1024;

            if (ie.code == ABS_Y)
                ts_y = ie.value * 480 / 600;
        }

        if (ie.type == EV_KEY && ie.code == BTN_TOUCH)
        {
            ts_sta = ie.value;

            // 触摸屏点击后的释放状态
            if (ts_sta == 0)
            {
                if (ts_y >= 0 && ts_y <= 65)
                {
                    if (ts_x <= 73)
                    {
                        break; // 退出
                    }

                    if (ts_x >= 73 && ts_x < 140)
                        pen_color = RGB(255, 255, 255); // 橡皮擦

                    if (ts_x >= 140 && ts_x < 207) // 一键清除
                    {
                        for (int i = 65; i < 480; i++)
                        {
                            for (int j = 0; j < 800; j++)
                            {
                                lcd_fill(j, i, 1, 1, RGB(255, 255, 255));
                            }
                        }
                        continue;
                    }

                    if (ts_x >= 207 && ts_x <= 275)
                    {
                        printf("画圆\n");
                        lcd_draw_circle(400, 260, r, pen_color);
                    }

                    if (ts_x >= 275 && ts_x <= 323 && r < 150)
                        r += 5;

                    if (ts_x >= 327 && ts_x < 377)
                        r -= 5;

                    // 画笔颜色选择
                    if (ts_x >= 380 && ts_x <= 427)
                        pen_color = RGB(0, 0, 0); // 黑色

                    if (ts_x >= 431 && ts_x < 481)
                        pen_color = RGB(255, 0, 0); // 红色

                    if (ts_x >= 485 && ts_x <= 535)
                        pen_color = RGB(255, 127, 39); // 橙色

                    if (ts_x >= 539 && ts_x <= 587)
                        pen_color = RGB(255, 242, 0); // 黄色

                    if (ts_x >= 591 && ts_x < 641)
                        pen_color = RGB(34, 177, 76); // 绿色

                    if (ts_x >= 644 && ts_x <= 693)
                        pen_color = RGB(0, 162, 232); // 蓝色

                    if (ts_x >= 696 && ts_x < 746)
                        pen_color = RGB(63, 72, 204); // 深蓝色

                    if (ts_x >= 750 && ts_x <= 800)
                        pen_color = RGB(163, 73, 162); // 紫色

                    continue;
                }
            }
        }
        if (ts_y > 65 && ts_sta)
            lcd_fill(ts_x, ts_y, r, r, pen_color);
    }

    return 1;
}

char bmp_buf[800 * 480 * 3] = {0}; // 用于接收图片颜色数据

// 读取刮刮乐的颜色数据
int *lcd_draw_ggl(const char *pathname, int x, int y)
{
    // 打开图片文件
    int fd_bmp = open(pathname, O_RDWR);
    if (fd_bmp == -1)
    {
        printf("open %s fail\n", pathname);
        return NULL;
    }

    // 位图文件头
    // BITMAPFILEHEADER 结构体别名,定义一个名为file_head的结构体变量
    BITMAPFILEHEADER file_head;
    read(fd_bmp, &file_head, sizeof(file_head)); // 光标自动移动到位图信息段头部
    // printf("%s图片大小%d\n", pathconf, file_head.bfSize);

    // 位图信息段,获取位图的高度、宽度、颜色深度
    // BITMAPINFOHEADER 结构体别名,定义一个名为info_head的结构体变量
    BITMAPINFOHEADER info_head;
    read(fd_bmp, &info_head, sizeof(info_head)); // 光标自动移动到RGB数据头部
    // // 宽(就是x)info_head.biWidth    高(就是y)info_head.biHeight
    // printf("%s图片尺寸:宽%d 高%d\n", pathconf, info_head.biWidth, info_head.biHeight);
    // printf("%s图片颜色深度:%d\n", pathname, info_head.biBitCount);

    // 读取RGB颜色数据
    read(fd_bmp, bmp_buf, info_head.biWidth * info_head.biHeight * info_head.biBitCount / 8);

    // 关闭文件
    close(fd_bmp);

    // 开始显示图片
    int x_s = x;
    int y_s = y;
    int x_e = x_s + info_head.biWidth;
    int y_e = y_s + info_head.biHeight;

    // 防止超出内存
    if (x_e > 800 || y_e > 480)
    {
        printf("%s: 图片尺寸超出范围\n", pathname);
        return NULL;
    }

    int i = 0;
    int *lcdbuff = malloc(y_e * x_e * 4);
    if (lcdbuff == NULL)
    {
        printf("malloc failed\n");
        return NULL;
    }

    // 循环读取RGB颜色数据并存入lcdbuff
    int x_pos, y_pos;
    for (y_pos = y_e - 1; y_pos >= y_s; y_pos--)
    {
        for (x_pos = x_s; x_pos < x_e && x_pos < 800; x_pos++, i += 3)
        {
            *(lcdbuff + (y_pos * 800) + x_pos) = (bmp_buf[i + 2] << 16) | (bmp_buf[i + 1] << 8) | bmp_buf[i];
        }
    }

    return lcdbuff;
}

// 读取刮刮乐颜色数据;
void lcd_fill_ggl(int x, int y, int *lcdbuff) // 显示刮刮乐的图片
{
    int i;
    int j;

    if (x + 20 > 800)
    {
        x = 780;
    }
    if (y + 20 > 480)
    {
        y = 460;
    }

    for (i = y - 20; i <= y + 20; i++) // 循环画点
    {
        for (j = x - 20; j <= x + 20; j++)
        {
            if ((i - y) * (i - y) + (j - x) * (j - x) <= 400)
                ; // 判断是否在圆形内
            {
                int color = *(lcdbuff + (i * 800) + j); // 获取颜色
                lcd_draw_point(j, i, color);            // 画点
            }
        }
    }
}

int icon_app4_func(void) // 进入刮刮乐
{
    lcd_draw_bmp("picture/lottery/prompt.bmp", 0, 0); // 显示提示
    sleep(3);

    printf("进入刮刮乐成功\n");

    // 定义个数组指针来存储刮刮乐的图片
    const char *bmp_lottery_tbl[] = {
        "picture/lottery/1.bmp",
        "picture/lottery/2.bmp",
        "picture/lottery/3.bmp",
        "picture/lottery/4.bmp",
        "picture/lottery/5.bmp",
        "picture/lottery/6.bmp",
        "picture/lottery/7.bmp",
        "picture/lottery/8.bmp",
        "picture/lottery/9.bmp",
        "picture/lottery/10.bmp",
    };

    int x_press, y_press;
    int ts_x, ts_y, ts_sta;     // 触摸屏的x和y坐标,ts_sta为按压状态
    int rt;                     // 用于接收ts_read函数的返回值,0为成功,-1为失败
    unsigned int lottery_color; // 定义一个变量来存储刮刮乐的颜色

    struct input_event buf; // 定义一个结构体变量来存储触摸屏的事件

    int fd_ts = open("/dev/input/event0", O_RDWR);
    if (fd_ts < 0)
    {
        perror("open /dev/input/event0");
        return -1;
    }

refresh:
    lcd_draw_bmp("picture/lottery/lottery.bmp", 0, 0); // 显示彩票界面

    srand((unsigned int)time(NULL)); // 初始化随机数种子
    int i = rand() % 10;             // 随机数

    int *lcdbuff = lcd_draw_ggl(bmp_lottery_tbl[i], 0, 80); // 获取图片的指针

    ts_open("/dev/input/event0");
    while (1)
    {
        rt = read(g_fd_ts, &buf, sizeof(buf));
    printf("ts_x:%d ts_y:%d ts_sta:%d\n", ts_x, ts_y, ts_sta);

        if (rt < 0)
            continue;

        // 判断触摸屏的事件类型
        if (buf.type == EV_ABS) // 获取触摸屏的坐标
        {
            if (buf.code == ABS_X)
            {
                ts_x = buf.value * 800 / 1024;
                if (ts_x > 800) // 防止超出屏幕
                {
                    continue;
                }
            }

            if (buf.code == ABS_Y) //
            {
                ts_y = buf.value * 480 / 600;
                if (ts_y > 480) // 防止超出屏幕
                {
                    continue;
                }
            }
        }

        if (buf.type == EV_KEY && buf.code == BTN_TOUCH)
        {
            ts_sta = buf.value; // 获取按压状态

            // 触摸屏点击后的释放状态
            if (ts_sta == 0)
            {
                if (ts_x >= 0 && ts_x <= 80 && ts_y >= 0 && ts_y <= 80)
                {
                    printf("返回主界面\n");
                    return 1;
                }

                if (ts_x >= 650 && ts_x <= 720 && ts_y >= 0 && ts_y <= 80)
                {
                    printf("清屏\n");
                    lcd_draw_bmp(bmp_lottery_tbl[i], 0, 80);
                }

                if (ts_x >= 720 && ts_x <= 800 && ts_y >= 0 && ts_y <= 80)
                {
                    printf("刷新图片\n");
                    goto refresh;
                }

                else // 防止超出屏幕
                {
                    continue;
                }
            }

            // 判断是否点击刮刮乐
            // if (ts_y > 80 && ts_sta)
        }
        
        if ((ts_y - 20) >= 75 && (ts_y + 20) <= 479 && (ts_x - 20) >= 0 && (ts_x + 20) <= 799 && ts_sta)
        {
            lcd_fill_ggl(ts_x, ts_y, lcdbuff);
        }
    }
    ts_close();
    return 1;
}

menu_t menu_startup = {
    .name = "开机界面",
    .fun = menu_startup_func,
    .parent = NULL,
    .child = &menu_login,
};

menu_t menu_login = {
    .name = "登录界面",
    .fun = menu_login_func,
    .parent = &menu_startup,
    .child = &menu_main,
};

menu_t menu_main = {
    .name = "主界面",
    .fun = menu_main_func,
    .parent = &menu_login,
    .child = NULL,
};

int main(int argc, char **argv)
{
    menu_t *pmenu = &menu_startup;

    // 打开LCD设备
    lcd_open("/dev/fb0");

    // 打开触摸屏设备
    ts_open("/dev/input/event0");

    while (1)
    {
        if (pmenu->fun)
        {
            printf("执行%s\n", pmenu->name);

            // 如果函数执行完毕,返回值为1,则进入父菜单
            if (pmenu->fun())
            {
                pmenu = pmenu->parent;

                continue;
            }
        }

        if (pmenu->child == NULL)
        {
            printf("没有子菜单\n");
            printf("重新进入开机界面\n");
            pmenu = &menu_startup;
            continue;
        }
        // 得到子菜单1
        pmenu = pmenu->child;
    }

    // 关闭LCD设备
    lcd_close();

    // 关闭触摸屏
    ts_close();

    // 正确的返回
    return 0;
}

标签:picture,相册,int,画图,ts,bmp,pos,&&,刮乐
From: https://blog.csdn.net/2401_86344273/article/details/142148571

相关文章

  • python画图|3D直方图基础教程
    前述已经完成了直方图和3D图的基本学习,链接如下:直方图:python画图|水平直方图绘制-CSDN博客3D图:python画图|水平直方图绘制-CSDN博客现在我们尝试把二者结合,画3D直方图。【1】官网教程首先,依然是来到官网,链接如下;Demoof3Dbarcharts—Matplotlib3.9.2documentatio......
  • python画图|极坐标中画散点图
    python极坐标画图时,不仅可以画实线图,也可以画散点图。实线图画法如下述链接。python画图|极坐标画图基础教程-CSDN博客今天我们一起学习一下散点图画法。【1】官网教程首先依然是导航到官网,乖乖学习官网教程:Scatterplotonpolaraxis—Matplotlib3.9.2documentatio......
  • python学习(一)turtle画图
    一些常用的函数:1)turtle.pensize():设置线条的粗细;2)turtle.speed():设置绘制的速度,1-10,1最慢,10最快;3)turtle.begin_fill():准备开始填充图形;4)turtle.circle(50,steps=3):circle函数在之前用到过,是画一个半径为radius的圆,这里是扩展,steps表示在半径为50的圆内的内置steps多边形;5)t......
  • python画图|极坐标画图基础教程
    前述已经学习了直方图、3D图、实现图、散点图等多种图形画法,它们都位于常规的直角坐标系,今天我们尝试探索新的方法:极坐标画图。【1】官网教程按照惯例,还是乖乖打开官网教程,链接如下:https://matplotlib.org/stable/gallery/pie_and_polar_charts/polar_demo.html打开后我们......
  • Star 39.9k太方便了!程序员的作图利器:draw.io!敲代码画图两不误!
    现在身处职场中,作为一名埋头苦干的打工人,不论是什么工种,不会做图有时候还真‘活不下去’。当然工厂拧螺丝就另说了!我是发现不论你是产品设计、运维、测试,还是程序员还是摆脱不了技术之外的事情,就比如制作各种图表,平时有平台/工具流程图,项目上有各种上项演示图表,私下其他部......
  • Axure的画图规范说明
    Axure的画图规范说明自定义网页整体尺寸:1366x768;色值:# F8F9FD顶部一级导航:1366x48;色值:# 1D8FFF左右布局,左侧边导航:768x194;色值:# 191A23侧边导航栏宽高:194x768左侧一级菜单宽高:194x40/边距:16,字号:14,色值:#FFFFFF,图标:14x14,二级导航宽194w,高40h,一二级菜单文字相差一个文......
  • 【CanMV K230】画图,画它个多啦A梦
    【CanMVK230】画图,画它个多啦A梦image对象构造函数使用方法画线段image.draw_line画矩形image.draw_rectangle画圆image.draw_circle绘制椭圆image.draw_ellipse画箭头image.draw_arrow画十字交叉image.draw_cross写字符image.draw_string写字符,支持中文image.d......
  • python画图|垂线标记系列
    进行了一段时间的直方图学习之后,发现python的matplo居然还支持画垂线标记图,赶紧把它记录下来。直方图绘制教程见下述链接:【a】直方图绘制基础教程:python画图|直方图绘制教程-CSDN博客【b】直方图绘制进阶教程:python画图|直方图绘制教程进阶-CSDN博客【c】堆叠直方图绘制......
  • python plt相关画图
    设置坐标轴粗细ax=plt.gca()ax.spines['bottom'].set_linewidth(2);#设置底部坐标轴的粗细ax.spines['left'].set_linewidth(2);ax.spines['right'].set_linewidth(2);ax.spines['top'].set_linewidth(2);设置图例与坐标轴plt.legend(prop={'si......
  • java-swing画图
    MyRect.javapackageMain;publicclassMyRect{ publicintx=0; publicinty=0; publicMyRgbrgb=newMyRgb(); longcreatetime=0; publicMyRect(){ } publicMyRect(intmx,intmy,MyRgbmyrgb,longtime){ x=mx; y=my; rgb=......