Linux应用学习-读取jpg显示在LCD上

代码1(单纯显示图片)

复制代码
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <string.h>
#include <linux/fb.h>
#include <sys/mman.h>
#include <jpeglib.h>
#include <tslib.h>

// jpeg图像显示,可以通过触摸屏移动图片

typedef struct rgb888_color{
    unsigned char red;
    unsigned char green;
    unsigned char blue;
}__attribute__((packed)) rgb888_color_t;

static int lcd_width;
static int lcd_height;
static unsigned short *screen_base = NULL;  // 映射后的显存首地址
static unsigned long line_length;           // LCD每一行的字节数
static unsigned int bpp;                    // 像素深度

// 图像相关全局变量
static unsigned short *image_buffer = NULL; // 存储整张图片的RGB565数据
static int image_width = 0;                 // 图片宽度
static int image_height = 0;                // 图片高度
static int offset_x = 0;                    // 当前显示区域的X偏移
static int offset_y = 0;                    // 当前显示区域的Y偏移

/**
 * 将图片缓冲区中指定区域显示到LCD
 */
static void display_image_region(void)
{
    int y;
    unsigned short *src_line;
    unsigned short *dst_line;
    int copy_width, copy_height;

    // 计算实际要显示的区域大小
    copy_width = (image_width - offset_x) < lcd_width ? (image_width - offset_x) : lcd_width;
    copy_height = (image_height - offset_y) < lcd_height ? (image_height - offset_y) : lcd_height;

    // 清屏(防止图片边缘有残留)
    memset(screen_base, 0x00, line_length * lcd_height);

    // 逐行复制图像数据到显存
    for(y = 0; y < copy_height; y++){
        src_line = image_buffer + (offset_y + y) * image_width + offset_x;
        dst_line = screen_base + y * lcd_width;
        memcpy(dst_line, src_line, copy_width * sizeof(unsigned short));
    }
}

/**
 * 解码JPEG图片到内存缓冲区
 */
static int decode_jpeg_to_buffer(const char *path)
{
    struct jpeg_decompress_struct cinfo;
    struct jpeg_error_mgr jerr;
    FILE *jpeg_file = NULL;
    rgb888_color_t *jpeg_line_buffer = NULL;
    int i;

    cinfo.err = jpeg_std_error(&jerr);

    jpeg_file = fopen(path, "r");
    if(NULL == jpeg_file){
        perror("fopen");
        return -1;
    }

    // 创建jpeg解码对象
    jpeg_create_decompress(&cinfo);
    jpeg_stdio_src(&cinfo, jpeg_file);
    jpeg_read_header(&cinfo, TRUE);

    // 保存图片尺寸
    image_width = cinfo.image_width;
    image_height = cinfo.image_height;
    printf("JPEG图像大小: %d x %d\n", image_width, image_height);
    printf("LCD分辨率: %d x %d\n", lcd_width, lcd_height);

    // 设置解码参数
    cinfo.out_color_space = JCS_RGB;
    jpeg_start_decompress(&cinfo);

    // 分配整张图片的缓冲区(RGB565格式)
    image_buffer = malloc(image_width * image_height * sizeof(unsigned short));
    if(NULL == image_buffer){
        perror("malloc image_buffer");
        fclose(jpeg_file);
        jpeg_destroy_decompress(&cinfo);
        return -1;
    }

    // 分配行缓冲区
    jpeg_line_buffer = malloc(cinfo.output_components * cinfo.output_width);
    if(NULL == jpeg_line_buffer){
        perror("malloc jpeg_line_buffer");
        free(image_buffer);
        image_buffer = NULL;
        fclose(jpeg_file);
        jpeg_destroy_decompress(&cinfo);
        return -1;
    }

    // 逐行解码并转换为RGB565存储到缓冲区
    while(cinfo.output_scanline < cinfo.output_height){
        unsigned short *dst = image_buffer + cinfo.output_scanline * image_width;
        jpeg_read_scanlines(&cinfo, (unsigned char **)&jpeg_line_buffer, 1);
        
        // RGB888转RGB565
        for(i = 0; i < image_width; i++){
            dst[i] = ((jpeg_line_buffer[i].red & 0xF8) << 8) |
                     ((jpeg_line_buffer[i].green & 0xFC) << 3) |
                     ((jpeg_line_buffer[i].blue & 0xF8) >> 3);
        }
    }

    // 清理
    jpeg_finish_decompress(&cinfo);
    jpeg_destroy_decompress(&cinfo);
    fclose(jpeg_file);
    free(jpeg_line_buffer);

    printf("图片解码完成,缓冲区大小: %d 字节\n", 
           image_width * image_height * (int)sizeof(unsigned short));
    return 0;
}

/**
 * 限制偏移量在有效范围内
 */
static void clamp_offset(void)
{
    int max_offset_x = image_width - lcd_width;
    int max_offset_y = image_height - lcd_height;

    // 确保最大偏移不为负(图片比屏幕小的情况)
    if(max_offset_x < 0) max_offset_x = 0;
    if(max_offset_y < 0) max_offset_y = 0;

    // 限制偏移量范围
    if(offset_x < 0) offset_x = 0;
    if(offset_y < 0) offset_y = 0;
    if(offset_x > max_offset_x) offset_x = max_offset_x;
    if(offset_y > max_offset_y) offset_y = max_offset_y;
}

int main(int argc, char *argv[])
{
    struct fb_fix_screeninfo fb_fix;
    struct fb_var_screeninfo fb_var;
    unsigned int screen_size;
    int fd;
    struct tsdev *ts = NULL;
    struct ts_sample samp;
    int pressure = 0;       // 0表示松开
    int last_x = 0, last_y = 0;  // 上一次触摸坐标

    /* 校验传参 */
    if(2 != argc){
        printf("Usage: %s <jpeg image path>\n", argv[0]);
        return -1;
    }

    /* 打开并配置触摸屏设备 */
    ts = ts_setup(NULL, 0);
    if(NULL == ts){
        fprintf(stderr, "ts_setup error\n");
        exit(EXIT_FAILURE);
    }

    /* 打开framebuffer设备 */
    fd = open("/dev/fb0", O_RDWR);
    if(fd < 0){
        perror("open fb0");
        ts_close(ts);
        exit(EXIT_FAILURE);
    }

    /* 获取屏幕信息 */
    ioctl(fd, FBIOGET_VSCREENINFO, &fb_var);
    ioctl(fd, FBIOGET_FSCREENINFO, &fb_fix);

    line_length = fb_fix.line_length;
    bpp = fb_var.bits_per_pixel;
    lcd_width = fb_var.xres;
    lcd_height = fb_var.yres;
    screen_size = line_length * fb_var.yres;

    /* 将显示缓冲区映射到进程地址空间 */
    screen_base = mmap(NULL, screen_size, PROT_WRITE, MAP_SHARED, fd, 0);
    if(MAP_FAILED == (void *)screen_base){
        perror("mmap");
        close(fd);
        ts_close(ts);
        exit(EXIT_FAILURE);
    }

    /* 清屏 */
    memset(screen_base, 0x00, screen_size);

    /* 解码JPEG图片到内存缓冲区 */
    if(decode_jpeg_to_buffer(argv[1]) < 0){
        munmap(screen_base, screen_size);
        close(fd);
        ts_close(ts);
        exit(EXIT_FAILURE);
    }

    /* 初始显示 */
    offset_x = 0;
    offset_y = 0;
    display_image_region();

    printf("\n触摸屏滑动控制图片移动\n");
    printf("向左滑动 -> 图片向右移动(看到图片左边部分)\n");
    printf("向右滑动 -> 图片向左移动(看到图片右边部分)\n");
    printf("向上滑动 -> 图片向下移动(看到图片上边部分)\n");
    printf("向下滑动 -> 图片向上移动(看到图片下边部分)\n");

    /* 主循环:读取触摸事件 */
    for(;;){
        if(ts_read(ts, &samp, 1) < 0){
            fprintf(stderr, "ts_read error\n");
            break;
        }

        if(samp.pressure){  // 当前按下
            if(pressure){   // 上一次也是按下状态 -> 滑动中
                int delta_x = samp.x - last_x;
                int delta_y = samp.y - last_y;

                // 手指向右滑,图片向左移(offset_x减小)
                // 手指向下滑,图片向上移(offset_y减小)
                // 这里取反实现"拖拽"效果
                offset_x -= delta_x;
                offset_y -= delta_y;

                // 限制偏移范围
                clamp_offset();

                // 刷新显示
                display_image_region();

                printf("滑动: delta(%d,%d) -> offset(%d,%d)\n", 
                       delta_x, delta_y, offset_x, offset_y);
            } else {
                // 刚按下
                printf("按下: (%d,%d)\n", samp.x, samp.y);
            }
            // 更新上次坐标
            last_x = samp.x;
            last_y = samp.y;
        } else {
            if(pressure){
                printf("松开\n");
            }
        }
        pressure = samp.pressure;
    }

    /* 清理退出 */
    if(image_buffer){
        free(image_buffer);
    }
    munmap(screen_base, screen_size);
    close(fd);
    ts_close(ts);
    exit(EXIT_SUCCESS);
}

代码2(增加触控移动功能)

复制代码
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <string.h>
#include <linux/fb.h>
#include <sys/mman.h>
#include <jpeglib.h>
#include <tslib.h>

// jpeg图像显示,可以通过触摸屏移动图片

typedef struct rgb888_color{
    unsigned char red;
    unsigned char green;
    unsigned char blue;
}__attribute__((packed)) rgb888_color_t;

static int lcd_width;
static int lcd_height;
static unsigned short *screen_base = NULL;  // 映射后的显存首地址
static unsigned long line_length;           // LCD每一行的字节数
static unsigned int bpp;                    // 像素深度
static unsigned int screen_size;            // 显存大小

// 图像相关全局变量
static unsigned short *image_buffer = NULL; // 存储整张图片的RGB565数据
static unsigned short *back_buffer = NULL;  // 双缓冲:后台缓冲区
static int image_width = 0;                 // 图片宽度
static int image_height = 0;                // 图片高度
static int offset_x = 0;                    // 当前显示区域的X偏移
static int offset_y = 0;                    // 当前显示区域的Y偏移

// 移动控制参数
#define MOVE_THRESHOLD  5   // 移动阈值(像素),超过才刷新
#define MOVE_SCALE      1   // 移动缩放因子(可调整灵敏度)

/**
 * 将图片缓冲区中指定区域显示到LCD(双缓冲版本)
 */
static void display_image_region(void)
{
    int y;
    unsigned short *src_line;
    unsigned short *dst_line;
    int copy_width, copy_height;
    int start_x = 0, start_y = 0;  // LCD上的起始位置

    // 计算实际要显示的区域大小
    copy_width = (image_width - offset_x) < lcd_width ? (image_width - offset_x) : lcd_width;
    copy_height = (image_height - offset_y) < lcd_height ? (image_height - offset_y) : lcd_height;

    // 先在后台缓冲区绘制(填充黑色背景)
    memset(back_buffer, 0x00, lcd_width * lcd_height * sizeof(unsigned short));

    // 如果图片比屏幕小,居中显示
    if(image_width < lcd_width){
        start_x = (lcd_width - image_width) / 2;
        copy_width = image_width;
    }
    if(image_height < lcd_height){
        start_y = (lcd_height - image_height) / 2;
        copy_height = image_height;
    }

    // 逐行复制图像数据到后台缓冲区
    for(y = 0; y < copy_height; y++){
        src_line = image_buffer + (offset_y + y) * image_width + offset_x;
        dst_line = back_buffer + (start_y + y) * lcd_width + start_x;
        memcpy(dst_line, src_line, copy_width * sizeof(unsigned short));
    }

    // 一次性将后台缓冲区复制到显存(避免撕裂)
    for(y = 0; y < lcd_height; y++){
        memcpy(screen_base + y * (line_length / sizeof(unsigned short)),
               back_buffer + y * lcd_width,
               lcd_width * sizeof(unsigned short));
    }
}

/**
 * 解码JPEG图片到内存缓冲区
 */
static int decode_jpeg_to_buffer(const char *path)
{
    struct jpeg_decompress_struct cinfo;
    struct jpeg_error_mgr jerr;
    FILE *jpeg_file = NULL;
    rgb888_color_t *jpeg_line_buffer = NULL;
    int i;

    cinfo.err = jpeg_std_error(&jerr);

    jpeg_file = fopen(path, "r");
    if(NULL == jpeg_file){
        perror("fopen");
        return -1;
    }

    // 创建jpeg解码对象
    jpeg_create_decompress(&cinfo);
    jpeg_stdio_src(&cinfo, jpeg_file);
    jpeg_read_header(&cinfo, TRUE);

    // 保存图片尺寸
    image_width = cinfo.image_width;
    image_height = cinfo.image_height;
    printf("JPEG图像大小: %d x %d\n", image_width, image_height);
    printf("LCD分辨率: %d x %d\n", lcd_width, lcd_height);

    // 设置解码参数
    cinfo.out_color_space = JCS_RGB;
    jpeg_start_decompress(&cinfo);

    // 分配整张图片的缓冲区(RGB565格式)
    image_buffer = malloc(image_width * image_height * sizeof(unsigned short));
    if(NULL == image_buffer){
        perror("malloc image_buffer");
        fclose(jpeg_file);
        jpeg_destroy_decompress(&cinfo);
        return -1;
    }

    // 分配行缓冲区
    jpeg_line_buffer = malloc(cinfo.output_components * cinfo.output_width);
    if(NULL == jpeg_line_buffer){
        perror("malloc jpeg_line_buffer");
        free(image_buffer);
        image_buffer = NULL;
        fclose(jpeg_file);
        jpeg_destroy_decompress(&cinfo);
        return -1;
    }

    // 逐行解码并转换为RGB565存储到缓冲区
    while(cinfo.output_scanline < cinfo.output_height){
        unsigned short *dst = image_buffer + cinfo.output_scanline * image_width;
        jpeg_read_scanlines(&cinfo, (unsigned char **)&jpeg_line_buffer, 1);
        
        // RGB888转RGB565
        for(i = 0; i < image_width; i++){
            dst[i] = ((jpeg_line_buffer[i].red & 0xF8) << 8) |
                     ((jpeg_line_buffer[i].green & 0xFC) << 3) |
                     ((jpeg_line_buffer[i].blue & 0xF8) >> 3);
        }
    }

    // 清理
    jpeg_finish_decompress(&cinfo);
    jpeg_destroy_decompress(&cinfo);
    fclose(jpeg_file);
    free(jpeg_line_buffer);

    printf("图片解码完成,缓冲区大小: %d 字节\n", 
           image_width * image_height * (int)sizeof(unsigned short));
    return 0;
}

/**
 * 限制偏移量在有效范围内
 */
static void clamp_offset(void)
{
    int max_offset_x = image_width - lcd_width;
    int max_offset_y = image_height - lcd_height;

    // 确保最大偏移不为负(图片比屏幕小的情况)
    if(max_offset_x < 0) max_offset_x = 0;
    if(max_offset_y < 0) max_offset_y = 0;

    // 限制偏移量范围
    if(offset_x < 0) offset_x = 0;
    if(offset_y < 0) offset_y = 0;
    if(offset_x > max_offset_x) offset_x = max_offset_x;
    if(offset_y > max_offset_y) offset_y = max_offset_y;
}

int main(int argc, char *argv[])

代码3(增加缩放功能)

复制代码
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <string.h>
#include <linux/fb.h>
#include <sys/mman.h>
#include <math.h>
#include <jpeglib.h>
#include <tslib.h>

// jpeg图像显示,可以通过触摸屏移动和缩放图片

typedef struct rgb888_color{
    unsigned char red;
    unsigned char green;
    unsigned char blue;
}__attribute__((packed)) rgb888_color_t;

static int lcd_width;
static int lcd_height;
static unsigned short *screen_base = NULL;  // 映射后的显存首地址
static unsigned long line_length;           // LCD每一行的字节数
static unsigned int bpp;                    // 像素深度
static unsigned int screen_size;            // 显存大小

// 图像相关全局变量
static unsigned short *image_buffer = NULL;   // 原始图片的RGB565数据
static unsigned short *scaled_buffer = NULL;  // 缩放后的图片数据
static unsigned short *back_buffer = NULL;    // 双缓冲:后台缓冲区
static int image_width = 0;                   // 原始图片宽度
static int image_height = 0;                  // 原始图片高度
static int scaled_width = 0;                  // 缩放后图片宽度
static int scaled_height = 0;                 // 缩放后图片高度
static int offset_x = 0;                      // 当前显示区域的X偏移
static int offset_y = 0;                      // 当前显示区域的Y偏移

// 缩放相关
static float scale_factor = 1.0f;             // 当前缩放比例
#define SCALE_MIN       0.25f                 // 最小缩放比例
#define SCALE_MAX       4.0f                  // 最大缩放比例

// 移动控制参数
#define MOVE_THRESHOLD  5                     // 移动阈值(像素)
#define PINCH_THRESHOLD 2                     // 缩放阈值(像素距离变化)

// 多点触摸最大支持数
#define MAX_SLOTS       5

/**
 * 计算两点之间的距离
 */
static float calc_distance(int x1, int y1, int x2, int y2)
{
    float dx = (float)(x2 - x1);
    float dy = (float)(y2 - y1);
    return sqrtf(dx * dx + dy * dy);
}

/**
 * 对原始图片进行缩放,生成缩放后的图片数据
 * 使用最近邻插值法
 */
static int scale_image(void)
{
    int x, y;
    int src_x, src_y;
    
    // 计算缩放后的尺寸
    scaled_width = (int)(image_width * scale_factor);
    scaled_height = (int)(image_height * scale_factor);
    
    // 确保最小尺寸
    if(scaled_width < 1) scaled_width = 1;
    if(scaled_height < 1) scaled_height = 1;
    
    // 重新分配缩放缓冲区
    if(scaled_buffer){
        free(scaled_buffer);
    }
    scaled_buffer = malloc(scaled_width * scaled_height * sizeof(unsigned short));
    if(NULL == scaled_buffer){
        perror("malloc scaled_buffer");
        return -1;
    }
    
    // 最近邻插值缩放
    for(y = 0; y < scaled_height; y++){
        src_y = (int)(y / scale_factor);
        if(src_y >= image_height) src_y = image_height - 1;
        
        for(x = 0; x < scaled_width; x++){
            src_x = (int)(x / scale_factor);
            if(src_x >= image_width) src_x = image_width - 1;
            
            scaled_buffer[y * scaled_width + x] = image_buffer[src_y * image_width + src_x];
        }
    }
    
    return 0;
}

/**
 * 限制偏移量在有效范围内
 */
static void clamp_offset(void)
{
    int max_offset_x = scaled_width - lcd_width;
    int max_offset_y = scaled_height - lcd_height;

    if(max_offset_x < 0) max_offset_x = 0;
    if(max_offset_y < 0) max_offset_y = 0;

    if(offset_x < 0) offset_x = 0;
    if(offset_y < 0) offset_y = 0;
    if(offset_x > max_offset_x) offset_x = max_offset_x;
    if(offset_y > max_offset_y) offset_y = max_offset_y;
}

/**
 * 将缩放后的图片指定区域显示到LCD(双缓冲版本)
 */
static void display_image_region(void)
{
    int y;
    unsigned short *src_line;
    unsigned short *dst_line;
    int copy_width, copy_height;
    int start_x = 0, start_y = 0;

    if(NULL == scaled_buffer) return;

    // 计算实际要显示的区域大小
    copy_width = (scaled_width - offset_x) < lcd_width ? (scaled_width - offset_x) : lcd_width;
    copy_height = (scaled_height - offset_y) < lcd_height ? (scaled_height - offset_y) : lcd_height;

    // 填充黑色背景
    memset(back_buffer, 0x00, lcd_width * lcd_height * sizeof(unsigned short));

    // 如果缩放后图片比屏幕小,居中显示
    if(scaled_width < lcd_width){
        start_x = (lcd_width - scaled_width) / 2;
        copy_width = scaled_width;
        offset_x = 0;
    }
    if(scaled_height < lcd_height){
        start_y = (lcd_height - scaled_height) / 2;
        copy_height = scaled_height;
        offset_y = 0;
    }

    // 逐行复制图像数据到后台缓冲区
    for(y = 0; y < copy_height; y++){
        src_line = scaled_buffer + (offset_y + y) * scaled_width + offset_x;
        dst_line = back_buffer + (start_y + y) * lcd_width + start_x;
        memcpy(dst_line, src_line, copy_width * sizeof(unsigned short));
    }

    // 一次性将后台缓冲区复制到显存
    for(y = 0; y < lcd_height; y++){
        memcpy(screen_base + y * (line_length / sizeof(unsigned short)),
               back_buffer + y * lcd_width,
               lcd_width * sizeof(unsigned short));
    }
}

/**
 * 解码JPEG图片到内存缓冲区
 */
static int decode_jpeg_to_buffer(const char *path)
{
    struct jpeg_decompress_struct cinfo;
    struct jpeg_error_mgr jerr;
    FILE *jpeg_file = NULL;
    rgb888_color_t *jpeg_line_buffer = NULL;
    int i;

    cinfo.err = jpeg_std_error(&jerr);

    jpeg_file = fopen(path, "r");
    if(NULL == jpeg_file){
        perror("fopen");
        return -1;
    }

    jpeg_create_decompress(&cinfo);
    jpeg_stdio_src(&cinfo, jpeg_file);
    jpeg_read_header(&cinfo, TRUE);

    image_width = cinfo.image_width;
    image_height = cinfo.image_height;
    printf("JPEG图像大小: %d x %d\n", image_width, image_height);
    printf("LCD分辨率: %d x %d\n", lcd_width, lcd_height);

    cinfo.out_color_space = JCS_RGB;
    jpeg_start_decompress(&cinfo);

    image_buffer = malloc(image_width * image_height * sizeof(unsigned short));
    if(NULL == image_buffer){
        perror("malloc image_buffer");
        fclose(jpeg_file);
        jpeg_destroy_decompress(&cinfo);
        return -1;
    }

    jpeg_line_buffer = malloc(cinfo.output_components * cinfo.output_width);
    if(NULL == jpeg_line_buffer){
        perror("malloc jpeg_line_buffer");
        free(image_buffer);
        image_buffer = NULL;
        fclose(jpeg_file);
        jpeg_destroy_decompress(&cinfo);
        return -1;
    }

    while(cinfo.output_scanline < cinfo.output_height){
        unsigned short *dst = image_buffer + cinfo.output_scanline * image_width;
        jpeg_read_scanlines(&cinfo, (unsigned char **)&jpeg_line_buffer, 1);
        
        for(i = 0; i < image_width; i++){
            dst[i] = ((jpeg_line_buffer[i].red & 0xF8) << 8) |
                     ((jpeg_line_buffer[i].green & 0xFC) << 3) |
                     ((jpeg_line_buffer[i].blue & 0xF8) >> 3);
        }
    }

    jpeg_finish_decompress(&cinfo);
    jpeg_destroy_decompress(&cinfo);
    fclose(jpeg_file);
    free(jpeg_line_buffer);

    printf("图片解码完成,缓冲区大小: %d 字节\n", 
           image_width * image_height * (int)sizeof(unsigned short));
    return 0;
}

int main(int argc, char *argv[])
{
    struct fb_fix_screeninfo fb_fix;
    struct fb_var_screeninfo fb_var;
    int fd;
    struct tsdev *ts = NULL;
    struct ts_sample_mt **samp_mt = NULL;
    int max_slots;
    int i;
    
    // 触摸状态
    int touch_count = 0;           // 当前触摸点数量
    int last_touch_count = 0;      // 上一次触摸点数量
    
    // 单指移动
    int last_x = 0, last_y = 0;
    int accum_x = 0, accum_y = 0;
    
    // 双指缩放
    float last_distance = 0;

    /* 校验传参 */
    if(2 != argc){
        printf("Usage: %s <jpeg image path>\n", argv[0]);
        return -1;
    }

    /* 打开触摸屏(多点触摸模式) */
    ts = ts_setup(NULL, 0);
    if(NULL == ts){
        fprintf(stderr, "ts_setup error\n");
        exit(EXIT_FAILURE);
    }

    /* 打开framebuffer */
    fd = open("/dev/fb0", O_RDWR);
    if(fd < 0){
        perror("open fb0");
        ts_close(ts);
        exit(EXIT_FAILURE);
    }

    ioctl(fd, FBIOGET_VSCREENINFO, &fb_var);
    ioctl(fd, FBIOGET_FSCREENINFO, &fb_fix);

    line_length = fb_fix.line_length;
    bpp = fb_var.bits_per_pixel;
    lcd_width = fb_var.xres;
    lcd_height = fb_var.yres;
    screen_size = line_length * fb_var.yres;

    screen_base = mmap(NULL, screen_size, PROT_WRITE | PROT_READ, MAP_SHARED, fd, 0);
    if(MAP_FAILED == (void *)screen_base){
        perror("mmap");
        close(fd);
        ts_close(ts);
        exit(EXIT_FAILURE);
    }

    /* 分配后台缓冲区 */
    back_buffer = malloc(lcd_width * lcd_height * sizeof(unsigned short));
    if(NULL == back_buffer){
        perror("malloc back_buffer");
        munmap(screen_base, screen_size);
        close(fd);
        ts_close(ts);
        exit(EXIT_FAILURE);
    }

    /* 分配多点触摸采样缓冲区 */
    max_slots = MAX_SLOTS;
    samp_mt = malloc(sizeof(struct ts_sample_mt *));
    if(NULL == samp_mt){
        perror("malloc samp_mt");
        free(back_buffer);
        munmap(screen_base, screen_size);
        close(fd);
        ts_close(ts);
        exit(EXIT_FAILURE);
    }
    samp_mt[0] = calloc(max_slots, sizeof(struct ts_sample_mt));
    if(NULL == samp_mt[0]){
        perror("calloc samp_mt[0]");
        free(samp_mt);
        free(back_buffer);
        munmap(screen_base, screen_size);
        close(fd);
        ts_close(ts);
        exit(EXIT_FAILURE);
    }

    memset(screen_base, 0x00, screen_size);

    /* 解码JPEG图片 */
    if(decode_jpeg_to_buffer(argv[1]) < 0){
        free(samp_mt[0]);
        free(samp_mt);
        free(back_buffer);
        munmap(screen_base, screen_size);
        close(fd);
        ts_close(ts);
        exit(EXIT_FAILURE);
    }

    /* 初始缩放 */
    scale_factor = 1.0f;
    if(scale_image() < 0){
        free(image_buffer);
        free(samp_mt[0]);
        free(samp_mt);
        free(back_buffer);
        munmap(screen_base, screen_size);
        close(fd);
        ts_close(ts);
        exit(EXIT_FAILURE);
    }

    offset_x = 0;
    offset_y = 0;
    display_image_region();

    printf("\n=== 触摸屏图片查看器 ===\n");
    printf("单指滑动: 移动图片\n");
    printf("双指捏合: 缩小图片\n");
    printf("双指张开: 放大图片\n");
    printf("缩放范围: %.0f%% - %.0f%%\n", SCALE_MIN * 100, SCALE_MAX * 100);

    /* 主循环 */
    for(;;){
        int ret = ts_read_mt(ts, samp_mt, max_slots, 1);
        if(ret < 0){
            fprintf(stderr, "ts_read_mt error\n");
            break;
        }

        /* 统计当前有效触摸点 */
        touch_count = 0;
        int valid_slots[2] = {-1, -1};
        
        for(i = 0; i < max_slots; i++){
            if(samp_mt[0][i].valid && samp_mt[0][i].pressure > 0){
                if(touch_count < 2){
                    valid_slots[touch_count] = i;
                }
                touch_count++;
            }
        }

        if(touch_count == 1 && valid_slots[0] >= 0){
            /* ========== 单指模式:移动图片 ========== */
            int idx = valid_slots[0];
            int cur_x = samp_mt[0][idx].x;
            int cur_y = samp_mt[0][idx].y;
            
            if(last_touch_count == 1){
                // 持续单指滑动
                int delta_x = cur_x - last_x;
                int delta_y = cur_y - last_y;
                
                accum_x += delta_x;
                accum_y += delta_y;
                
                // 累积超过阈值才刷新
                if(abs(accum_x) >= MOVE_THRESHOLD || abs(accum_y) >= MOVE_THRESHOLD){
                    offset_x -= accum_x;
                    offset_y -= accum_y;
                    clamp_offset();
                    display_image_region();
                    accum_x = 0;
                    accum_y = 0;
                }
            } else {
                // 刚变成单指(从无触摸或双指切换),重置累积量
                accum_x = 0;
                accum_y = 0;
            }
            
            last_x = cur_x;
            last_y = cur_y;
        }
        else if(touch_count >= 2 && valid_slots[0] >= 0 && valid_slots[1] >= 0){
            /* ========== 双指模式:缩放图片 ========== */
            int idx0 = valid_slots[0];
            int idx1 = valid_slots[1];
            
            int x0 = samp_mt[0][idx0].x;
            int y0 = samp_mt[0][idx0].y;
            int x1 = samp_mt[0][idx1].x;
            int y1 = samp_mt[0][idx1].y;
            
            float cur_distance = calc_distance(x0, y0, x1, y1);
            
            // 计算缩放中心点(两指中点)
            int pinch_center_x = (x0 + x1) / 2;
            int pinch_center_y = (y0 + y1) / 2;
            
            if(last_touch_count >= 2 && last_distance > 0){
                // 持续双指操作 - 使用比例缩放,更自然更灵敏
                float distance_ratio = cur_distance / last_distance;
                
                // 只有距离变化超过阈值才处理
                if(fabsf(cur_distance - last_distance) >= PINCH_THRESHOLD){
                    // 计算缩放前中心点对应的图片坐标(相对位置)
                    float img_center_x = offset_x + pinch_center_x;
                    float img_center_y = offset_y + pinch_center_y;
                    float rel_x = img_center_x / scaled_width;
                    float rel_y = img_center_y / scaled_height;
                    
                    // 保存旧缩放比例
                    float old_scale = scale_factor;
                    
                    // 按比例缩放(关键修改:使缩放更自然)
                    scale_factor = old_scale * distance_ratio;
                    
                    // 限制缩放范围
                    if(scale_factor < SCALE_MIN) scale_factor = SCALE_MIN;
                    if(scale_factor > SCALE_MAX) scale_factor = SCALE_MAX;
                    
                    // 只有缩放比例真正改变时才重新生成图片
                    if(fabsf(scale_factor - old_scale) > 0.001f){
                        if(scale_image() == 0){
                            // 根据缩放中心调整偏移,使中心点保持不变
                            offset_x = (int)(rel_x * scaled_width) - pinch_center_x;
                            offset_y = (int)(rel_y * scaled_height) - pinch_center_y;
                            
                            clamp_offset();
                            display_image_region();
                            
                            printf("缩放: %.0f%% (%dx%d)\n", 
                                   scale_factor * 100, scaled_width, scaled_height);
                        }
                    }
                    
                    // 立即更新上次距离(关键:每次都更新,防止累积)
                    last_distance = cur_distance;
                }
            } else {
                // 刚变成双指,记录初始距离
                last_distance = cur_distance;
            }
        }
        else {
            /* ========== 无触摸 ========== */
            // 如果有累积的移动量,应用它
            if(last_touch_count == 1 && (abs(accum_x) > 0 || abs(accum_y) > 0)){
                offset_x -= accum_x;
                offset_y -= accum_y;
                clamp_offset();
                display_image_region();
            }
            
            // 重置状态
            last_distance = 0;
            accum_x = 0;
            accum_y = 0;
        }
        
        last_touch_count = touch_count;
    }

    /* 清理退出 */
    if(image_buffer) free(image_buffer);
    if(scaled_buffer) free(scaled_buffer);
    if(back_buffer) free(back_buffer);
    if(samp_mt[0]) free(samp_mt[0]);
    if(samp_mt) free(samp_mt);
    munmap(screen_base, screen_size);
    close(fd);
    ts_close(ts);
    exit(EXIT_SUCCESS);
}
相关推荐
名誉寒冰2 小时前
AI大模型-Prompt工程参考学习
人工智能·学习·大模型·prompt
狮子雨恋2 小时前
Python 多维数组学习示例
python·学习·numpy
我的xiaodoujiao2 小时前
使用 Python 语言 从 0 到 1 搭建完整 Web UI自动化测试学习系列 36--二次封装MySQL数据库连接操作
python·学习·测试工具·pytest
prettyxian2 小时前
【Linux】内核编织术:task_struct的动态网络
linux·运维·服务器
典孝赢麻崩乐急2 小时前
Redis复习----------Redis超高性能的原因
数据库·redis·学习·缓存
Danileaf_Guo2 小时前
OSPF路由引入的陷阱:为何Ubuntu上静态路由神秘消失?深挖FRR路由分类机制
linux·运维·网络·ubuntu·智能路由器
张某人的胡思乱想2 小时前
windows远程ubuntu
linux·运维·ubuntu
QT 小鲜肉2 小时前
【Linux命令大全】001.文件管理之mtoolstest命令(实操篇)
linux·运维·前端·笔记·microsoft
iconball2 小时前
个人用云计算学习笔记 --30 华为云存储云服务
运维·笔记·学习·华为云·云计算