It is possible to write a native C application, that opens the framebuffer (/dev/graphics/fb0) to extract bitmap data representing the screen surface image.
It is also possible to modify the framebuffer data and by doing so to do a low level drawing on screen (that is also very fast). The data must be then converted (aligned/reversed) to standard BMP format, and saved to disc. |
The framebuffer grabber:
static int get_framebuffer(GGLSurface *fb) { int fd; void *bits; fd = open("/dev/graphics/fb0", O_RDWR); if(fd < 0) { perror("cannot open fb0"); return -1; } if(ioctl(fd, FBIOGET_FSCREENINFO, &fi) < 0) { perror("failed to get fb0 info"); return -1; } if(ioctl(fd, FBIOGET_VSCREENINFO, &vi) < 0) { perror("failed to get fb0 info"); return -1; } //dumpinfo(&fi, &vi); bits = mmap(0, fi.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if(bits == MAP_FAILED) { perror("failed to mmap framebuffer"); return -1; } fb->version = sizeof(*fb); fb->width = vi.xres; fb->height = vi.yres; fb->stride = fi.line_length / (vi.bits_per_pixel >> 3); fb->data = bits; fb->format = GGL_PIXEL_FORMAT_RGB_565; fb++; fb->version = sizeof(*fb); fb->width = vi.xres; fb->height = vi.yres; fb->stride = fi.line_length / (vi.bits_per_pixel >> 3); fb->data = (void*) (((unsigned) bits) + vi.yres * vi.xres * 2); fb->format = GGL_PIXEL_FORMAT_RGB_565; return fd; }
The BITMAP former:
The first thing to do is to convert the Framebuffer's 16 bit data into
24 bit. We're not gaining extra-quality here, we're just "stretching"
the colors from:
5 bits blue : 2^5=32 blue color nuances
6 bits green : 2^6=64 green color nuances (in 16bit colors, green has
more color space since the human eye is more sensitive to green color)
5 bits red : 2^5=32 red color nuances
32x64x32 possible 16bits colors (2^16)
To:
8 bits blue : 2^8 = 256 color nuances
8 bits green : 2^8 = 256 color nuances
8 bits red : 2^8 = 256 color nuances
A total of 2^24 colors, but as I said we're just "stretching" the color intervals without any information gain.
//convert pixel data 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 *)gr_framebuffer[0].data)[i]; // RRRRRGGGGGGBBBBBB -> RRRRRRRRGGGGGGGGBBBBBBBB // in rgb24 color max is 2^8 per channel (*255/32 *255/64 *255/32) rgb24[3*i+2] = (255*(pixel16 & 0x001F))/ 32; //Blue rgb24[3*i+1] = (255*((pixel16 & 0x07E0) >> 5))/64; //Green rgb24[3*i] = (255*((pixel16 & 0xF800) >> 11))/32; //Red } } else if (depth == 24) { rgb24 = (uint8_t *) gr_framebuffer[0].data; } else { //free close(gr_fb_fd); exit(2); }; //save RGB 24 Bitmap int bytes_per_pixel = 3; BMPHEAD bh; memset ((char *)&bh,0,sizeof(BMPHEAD)); // sets everything to 0 //bh.filesize = calculated size of your file (see below) //bh.reserved = two zero bytes bh.headersize = 54L; // for 24 bit images bh.infoSize = 0x28L; // for 24 bit images bh.width = w; // width of image in pixels bh.depth = h; // height of image in pixels bh.biPlanes = 1; // for 24 bit images bh.bits = 8 * bytes_per_pixel; // for 24 bit images bh.biCompression = 0L; // no compression int bytesPerLine; bytesPerLine = w * bytes_per_pixel; // for 24 bit images //round up to a dword boundary if (bytesPerLine & 0x0003) { bytesPerLine |= 0x0003; ++bytesPerLine; } bh.filesize = bh.headersize + (long)bytesPerLine * bh.depth; FILE * bmpfile; //printf("Bytes per line : %d\n", bytesPerLine); bmpfile = fopen("screen.bmp", "wb"); if (bmpfile == NULL) { close(gr_fb_fd); exit(3); } fwrite("BM",1,2,bmpfile); fwrite((char *)&bh, 1, sizeof (bh), bmpfile); //fwrite(rgb24,1,w*h*3,bmpfile); char *linebuf; linebuf = (char *) calloc(1, bytesPerLine); if (linebuf == NULL) { fclose(bmpfile); close(gr_fb_fd); exit(4); }
Now the next thing to do is to align the BGR triplets and to switch order , to match the BMP RGB24 color format.
int line,x; for (line = h-1; line >= 0; line --) { // fill line linebuf with the image data for that line for( x =0 ; x < w; x++ ) { *(linebuf+x*bytes_per_pixel) = *(rgb24 + (x+line*w)*bytes_per_pixel+2); *(linebuf+x*bytes_per_pixel+1) = *(rgb24 + (x+line*w)*bytes_per_pixel+1); *(linebuf+x*bytes_per_pixel+2) = *(rgb24 + (x+line*w)*bytes_per_pixel+0); } // remember that the order is BGR and if width is not a multiple // of 4 then the last few bytes may be unused fwrite(linebuf, 1, bytesPerLine, bmpfile); } fclose(bmpfile); close(gr_fb_fd);
Download the code: NativeScreenCapture
Note that you are not allowed to distribute this code without visibly
indicating it's author (me) and the source (this page) and You may not
embedded it in commercial applications.
To compile the code, use the NDK, as presented in this article.
Running the code produces a screen.bmp file:
Hope this helps.