JZ2440 实现截图 保存为png格式

目的

有时候需要记录BUG现象、保存图片等需求,所以在 JZ2440 上实现截图的功能。
顺便记录一下 png 格式的保存方法,以便以后查阅。

准备

需要使用到 linpng 的库,编译安装见以下链接
交叉编译 libpng-1.6.37 zlib-1.2.11 简单使用

实现

截图功能比较简单,只需要获得一个事件,然后将显存的 RBG 格式,保存为 png 格式就可以了,
这里为了使用方便,决定使用板上按键 S5(EINT19)作为触发截图的按键,
所以分为两个部分,驱动层申请中断获取事件通知应用层,应用层获取事件保存截图。

1. 驱动层

不知道写啥,直接贴源码吧。

#include <linux/init.h>
#include <linux/fs.h>
#include <linux/kdev_t.h>
#include <linux/cdev.h>
#include <asm/uaccess.h>
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <asm/io.h>
#include <linux/irq.h>
#include <asm/irq.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>
#include <linux/poll.h>
#include <asm/atomic.h>
#include <mach/regs-gpio.h>
#include <linux/sched.h>
#include <linux/timer.h>
#include <asm/io.h>
typedef volatile unsigned int vuint32x;         // 寄存器等定义成这个
#define BUTTON_NUM (1)                          // 为扩展做准备
#define BUTTON_MODULE_NAME "screenshot_button"  // 设备名称
#define REG_GPGDAT 0x56000064                   // EINT19 所在的 GPIO 数据寄存器
static atomic_t canopen = ATOMIC_INIT(1);       // 用来确保只有一个应用程序打开该驱动
static int button_major;                        // 驱动主设备号
static dev_t button_dev_t;                      // 驱动设备号
static struct class* button_class;              // 创建类
static struct device* button_class_dev;         // 创建类中的设备,自动生成设备节点
static struct fasync_struct *button_fasync;     // 异步通知
static vuint32x * pGpgdat;                      // ioremap 后的寄存器地址
struct btn_desc_def {
    int irq_num;               // 输入中断号
    char* name;                // 名字
    struct timer_list timer;   // 为了消抖使用
};
static struct btn_desc_def btn_s[BUTTON_NUM] = {  // 支持的按键数组,为了方便只做一个
    {IRQ_EINT19 , BUTTON_MODULE_NAME, { }},
};
static irqreturn_t buttons_irq(int irqnum, void * data) // EINT19 中断函数
{
    struct btn_desc_def* btn = (struct btn_desc_def *)data; // 获取按键对象
    mod_timer(&btn->timer , jiffies + (HZ * 75 / 1000));    // 定时 75 ms
    return IRQ_HANDLED;
}
static void btn_timer_fun(unsigned long __input_data)
{
    //struct btn_desc_def * const btndesc = (struct btn_desc_def * const)__input_data;
    //struct timer_list * timer = &btndesc->timer;
    //static int drv_cnt = 0;printk("drv_cnt = %d\n",drv_cnt++);
    if (*pGpgdat & (1 << 11))  // 使用过程发现不消抖会触发多次,这里偷懒没有将该数据放入按键数组中
        return;                // 按键定时后,状态不是按下,直接返回
    kill_fasync(&button_fasync, SIGIO, POLL_IN); // 异步通知
}
static int button_module_open(struct inode *pnode, struct file *pfile)
{
    int ret, i;
    if(!atomic_dec_and_test(&canopen)) { // 确保只有一个应用程序打开该设备
        atomic_inc(&canopen);
        return -EBUSY;
    }
    printk("button_module_open button_major : %d\n", button_major);
    pGpgdat = ioremap(REG_GPGDAT, 4);
    for(i=0;i<BUTTON_NUM;i++) {
        ret = request_irq(btn_s[i].irq_num , buttons_irq, IRQF_TRIGGER_FALLING, 
            btn_s[i].name, &btn_s[i]);   // 申请中断
        init_timer(&btn_s[i].timer);     // 初始化定时器
        btn_s[i].timer.function = btn_timer_fun;         // 定时器执行函数
        btn_s[i].timer.data = (unsigned long)&btn_s[i];  // 定时器执行函数传入的参数
    }
    return 0;
}
static int button_module_release(struct inode * pnode, struct file *pfile)
{
    int i;
    printk("button_module_release button_major : %d\n", button_major);
    for(i=0;i<BUTTON_NUM;i++) {
        free_irq(btn_s[i].irq_num, &btn_s[i]);
        del_timer(&btn_s[i].timer);
    }
    iounmap(pGpgdat);
    atomic_inc(&canopen); // 可以让其他应用程序打开
    return 0;
}
static int button_module_fasync(int fd, struct file * file, int on)
{
    // 成功返回 0 ,失败返回 小于0
    return fasync_helper(fd, file, on, &button_fasync);
}
static struct file_operations button_module_fops = {
    .owner  = THIS_MODULE,
    .open   = button_module_open,
    .release= button_module_release,
    .fasync = button_module_fasync,
};
static int __init button_module_init(void)
{
    button_major = register_chrdev(0, BUTTON_MODULE_NAME, &button_module_fops);
    button_dev_t = MKDEV(button_major, 0);
    printk(BUTTON_MODULE_NAME " button_module_init button_major : %d\n", button_major);
    button_class = class_create(THIS_MODULE, BUTTON_MODULE_NAME);
    if (IS_ERR(button_class)) {
        printk("button_class error %ld\n", IS_ERR(button_class));
        return -1;
    }
    button_class_dev = device_create(button_class, NULL, 
        button_dev_t, NULL, BUTTON_MODULE_NAME);
    if (IS_ERR(button_class_dev)) {
        printk("button_class_dev error %ld\n",IS_ERR(button_class_dev));
        return -1;
    }
    return 0;
}
static void __exit button_module_exit(void)
{
    printk(BUTTON_MODULE_NAME" button_module_exit\n");
    unregister_chrdev(button_major,BUTTON_MODULE_NAME);
    device_destroy(button_class, button_dev_t);
    class_destroy(button_class);
}
module_init(button_module_init);
module_exit(button_module_exit);
MODULE_LICENSE("GPL");

2. 应用层

a. 获取显存

获取显示屏的信息,并且使用 mmap 函数来获取显存地址。

#include <linux/fb.h>
#include <sys/mman.h>
#define FB_DEVICE_NAME "/dev/fb0"         // 显示设备节点名称
static int g_fb_fd;                       // 显示设备文件描述符
static struct fb_var_screeninfo g_tFBVar; // 显示设备可变参数
static struct fb_fix_screeninfo g_tFBFix; // 显示设备固定参数
static unsigned char *g_pucFBMem;         // 显存地址
static unsigned int g_dwScreenSize;       // 显存大小 = xres * yres * bpp / 8
static unsigned int g_dwLineWidth;        // 一行长度 = xres * bpp / 8
static unsigned int g_dwPixelWidth;       // 像素长度 = bpp / 8
static int FBDeviceInit()
{
    int ret;
    g_fb_fd = open(FB_DEVICE_NAME, O_RDWR); // 可读可写
    ret = ioctl(g_fb_fd, FBIOGET_VSCREENINFO, &g_tFBVar);
    ret = ioctl(g_fb_fd, FBIOGET_FSCREENINFO, &g_tFBFix);
    g_dwScreenSize = g_tFBVar.xres * g_tFBVar.yres * g_tFBVar.bits_per_pixel / 8;
    g_pucFBMem = (unsigned char *)mmap(NULL , g_dwScreenSize, PROT_READ | PROT_WRITE, MAP_SHARED, g_fb_fd, 0); 
    g_dwLineWidth  = g_tFBVar.xres * g_tFBVar.bits_per_pixel / 8;
    g_dwPixelWidth = g_tFBVar.bits_per_pixel / 8;
    printf("g_pucFBMem = 0x%08X\n",(int)g_pucFBMem);
    printf("g_dwScreenSize = 0x%08X\n", g_dwScreenSize);
    printf("xres = %d yres = %d\n", g_tFBVar.xres, g_tFBVar.yres);
    printf("g_dwLineWidth = %d\ng_dwPixelWidth = %d\n", g_dwLineWidth, g_dwPixelWidth);
    return 0;
}

b. 使用 libpng 保存图片

根据 libpng 库中的 example.c 修改

#include <zlib.h>
#include <png.h>
static int screenshot(const char* file_name) // 保存文件名
{
    FILE *fp;
    png_structp png_ptr;  // png 对象
    png_infop info_ptr;   // png 文件头信息
    int row;
    // 打开文件
    fp = fopen(file_name, "wb");
    if (fp == NULL) {
        printf("fopen error !\n");
        return -1;
    }
    // 创建 png 对象,使用 longjmp 方法获取异常时后面三个参数填 NULL
    png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL,NULL);
    if (png_ptr == NULL) {
        printf("png_create_write_struct error !\n");
        fclose(fp);
        return -1;
    }
    // 创建 png 文件头部信息
    info_ptr = png_create_info_struct(png_ptr);
    if (info_ptr == NULL) {
        printf("png_create_info_struct error !\n");
        fclose(fp);
        png_destroy_write_struct(&png_ptr,  NULL);
        return -1;
    }
    // 设置异常捕获点
    if (setjmp(png_jmpbuf(png_ptr))) {
        /* If we get here, we had a problem writing the file. */
        printf("long jump error !\n");
        fclose(fp);
        png_destroy_write_struct(&png_ptr, &info_ptr);
        return -1;
    }
    // 告知 png 对象要写入的文件
    png_init_io(png_ptr, fp);
    // 设置 png 文件头部信息,这里保存的时 RGBA 文件
    png_set_IHDR(png_ptr, info_ptr, g_tFBVar.xres, g_tFBVar.yres, 8, PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE,PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
    // 将 png 头部信息写入文件
    png_write_info(png_ptr, info_ptr);
    // 我也不知道什么意思
    png_set_packing(png_ptr);
    // 只是用来显示一下刚才设置的信息
    //png_uint_32 width, height;
    //int bit_depth, color_type, interlace_type;
    //png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, NULL, NULL);
    //printf("\twidth=%d,height=%d,bit_depth=%d\n\tcolor_type=%d,interlace_type=%d\n", width, height, bit_depth, color_type, interlace_type);

    // png RGBA 格式一行数据,一像素需要四个字节
    png_bytep const rowbuf = malloc(g_tFBVar.xres << 2);
    png_bytep fbmem;    // 显存每行开始的地址
    png_uint_16 color;  // 这里偷懒了,没有对各种 bpp 进行判断,直接使用了 16bpp
    int i, j;
    for(row = 0;row < g_tFBVar.yres;row++) {
        // 获取行首地址
        fbmem = g_pucFBMem + row * g_tFBVar.xres * (g_tFBVar.bits_per_pixel >> 3);
        i = j = 0;
        while(i < g_tFBVar.xres * 2) {   // 将显存数据转换成 RGBA 格式
            color = (fbmem[i+1] << 8) | fbmem[i];  // 16bpp 颜色
            i += 2;
            rowbuf[j++] = (png_byte)((color >> 8) & 0xF8);
            rowbuf[j++] = (png_byte)((color >> 3) & 0xFC);
            rowbuf[j++] = (png_byte)((color << 3) & 0xF8);
            rowbuf[j++] = 0xFF;   // 透明度:0xFF 不透明
        }
        // 该行写入文件
        png_write_row(png_ptr, rowbuf);
    }
    free(rowbuf);
    // 写入 png 尾部信息
    png_write_end(png_ptr, info_ptr);
    png_destroy_write_struct(&png_ptr, &info_ptr);  // 销毁对象
    fclose(fp);  // 释放文件
    return 0;

}

3. 编译

编译时加上 -lz -lpng 指明所使用的库。

arm-linux-gcc -o screenshot screenshot.c -lz -lpng

测试

支持鼠标测试时的截图:
支持鼠标时的测试截图
预读文件同时拖拽的截图:
预读文件同时拖拽的截图

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值