如何从FrameBuffer捕获屏幕截图?

时间:2013-10-09 11:37:15

标签: android c linux framebuffer

我想从FrameBuffer中的Android抓取屏幕截图,我使用下面的代码,但只是得到了一个模糊的图像。我包含3个主要步骤。首先,从FrameBuffer读取数据和信息,其次,将原始数据转换为24位,第三,构造BITMAP结构并写入bmp文件。但我得到了模糊的图像,有人可以提供帮助吗?我会很感激。

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <linux/fb.h>
#include <sys/ioctl.h>

typedef unsigned char BYTE;
typedef unsigned short WORD;
typedef unsigned int DWORD;
typedef long LONG;

typedef struct tagBITMAPFILEHEADER {
    WORD  bfType;
    DWORD bfSize;
    WORD  bfReserved1;
    WORD  bfReserved2;
    DWORD bfOffBits;
}__attribute__((packed)) BITMAPFILEHEADER, *PBITMAPFILEHEADER;

typedef struct tagBITMAPINFOHEADER {
    DWORD biSize;
    LONG  biWidth;
    LONG  biHeight;
    WORD  biPlanes;
    WORD  biBitCount;
    DWORD biCompression;
    DWORD biSizeImage;
    LONG  biXPelsPerMeter;
    LONG  biYPelsPerMeter;
    DWORD biClrUsed;
    DWORD biClrImportant;
}__attribute__((packed)) BITMAPINFOHEADER, *PBITMAPINFOHEADER;

typedef struct tagRGBQUAD {
    BYTE rgbBlue;
    BYTE rgbGreen;
    BYTE rgbRed;
    BYTE rgbReserved;
}__attribute__((packed)) RGBQUAD;

#define FRAME_BUFFER_PATH                "/dev/graphics/fb0"

int take_screenshot(char *path)
{  
    int i;
    int img_fd, fb_fd;
    int data_size;
    char *img_buf;
    struct fb_var_screeninfo var_info;
    struct fb_fix_screeninfo fix_info;
    BITMAPFILEHEADER file_head;
    BITMAPINFOHEADER info_head;
    //RGBQUAD rgb_quad;

    /*open files*/
    fb_fd = open(FRAME_BUFFER_PATH, O_RDWR);
    if (img_fd < 0) {
            perror("open framebuff");
            return -1;
    }
    if (ioctl(fb_fd, FBIOGET_VSCREENINFO, &var_info) < 0) {
            perror("ioctl FBIOGET_VSCREENINFO");
            close(img_fd);
            return 0;
    }
    printf("xres %d, yres %d\n", var_info.xres, var_info.yres);

    if (ioctl(fb_fd, FBIOGET_FSCREENINFO, &fix_info)){
      debug("Error reading fixed information\n");
      close(img_fd);
            return 0;
    }

    img_fd = open(path, O_RDWR | O_CREAT, 0644);
    if (img_fd < 0)
    {
            perror("open image");
            close(img_fd);
            return -1;
    }


    data_size = var_info.xres*var_info.yres*(var_info.bits_per_pixel/8);
    /*initialize bmp structs*/
    file_head.bfType = 0x4d42;
    file_head.bfSize = sizeof(file_head) + sizeof(info_head) + data_size;
    file_head.bfReserved1 = 0;
    file_head.bfReserved2 = 0;
    file_head.bfOffBits = sizeof(file_head) + sizeof(info_head);

    info_head.biSize = sizeof(info_head);
    info_head.biWidth = var_info.xres;
    info_head.biHeight = -var_info.yres;
    info_head.biPlanes = 0;
    info_head.biBitCount = 24;
    info_head.biCompression = 0;
    info_head.biSizeImage = data_size;
    info_head.biXPelsPerMeter = 3780;
    info_head.biYPelsPerMeter = 3780;
    info_head.biClrUsed = 0;
    info_head.biClrImportant = 0;

    img_buf = (char *)malloc(data_size);
    if (img_buf == NULL)
    {
            printf("malloc failed!\n");
            close(fb_fd);
            close(img_fd);
            return -1;
    }

  /*read img data and */
  read(fb_fd, img_buf, data_size);

  write(img_fd, &file_head, sizeof(file_head));
  write(img_fd, &info_head, sizeof(info_head));

/*********************/
  int w, h;
  int depth;
  unsigned short *bits;

  w = var_info.xres;
  h = var_info.yres;
  depth = var_info.bits_per_pixel;

  uint8_t *rgb24;
  if (depth == 16) {
      rgb24 = (uint8_t *)malloc(w * h * 3);
      int i = 0;
      for ( ; i < w*h; i++) {
          uint16_t pixel16 = ((uint16_t *)img_buf)[i];
          // RRRRRGGGGGGBBBBBB -> RRRRRRRRGGGGGGGGBBBBBBBB
          // in rgb24 color max is 2^8 per channel (*255/32 *255/64 *255/32)
          rgb24[3*i+0]   = (255*(pixel16 & 0x001F))/ 32;    //Blue
          rgb24[3*i+1]   = (255*((pixel16 & 0x07E0) >> 5))/64;  //Green
          rgb24[3*i+2]   = (255*((pixel16 & 0xF800) >> 11))/32;   //Red
      }
  } else if (depth == 24) {
      rgb24 = (uint8_t *)img_buf;
  } else if (depth == 32) {
      //skip transparency channel
      rgb24 = (uint8_t *) malloc(w * h * 3);
      int i=0;
      for ( ; i <w*h; i++) {
          uint32_t pixel32 = ((uint32_t *)img_buf)[i];
          // in rgb24 color max is 2^8 per channel
          rgb24[3*i+2]   =  pixel32 & 0x000000FF;     //Blue
          rgb24[3*i+1]   = (pixel32 & 0x0000FF00) >> 8; //Green
          rgb24[3*i+0]   = (pixel32 & 0x00FF0000) >> 16;  //Red
      }
  } else {
  };

  write(img_fd, rgb24, w*h*3);

  close(fb_fd);
  close(img_fd);
  return 0;
}

enter image description here

1 个答案:

答案 0 :(得分:0)

有时帧缓冲区的行大小会大于分辨率。所以你不应该使用xres来确定下一行的开始位置。固定信息中保存了总内存大小属性,因此您可以将其除以yres。还有一个行间步长属性,但我不确定它是否有效。也许你可以尝试一下。