سؤال

I want to capture screenshot from FrameBuffer in Android, I use the code below, but just got a fuzzy image.I contains 3 main steps. first, read data and info from FrameBuffer, second, convert the raw data to 24 bits, third, construct the BITMAP structs and write to bmp file. But I got fuzzy images, does anyone could help?I will appreciate it.

#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

هل كانت مفيدة؟

المحلول

Sometime the framebuffer's row size will be larger than the resolution. So you shouldn't use the xres to determine where the next line begins. There is a total memory size attribute saved in the fixed info, so you could divide it with the yres. There is also a row-stride attribute but I'm not sure whether it works. Maybe you could try it.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top