代码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);
}