screencap.cpp revision 0b722fe9ce98d97dbcb6fefd170b85ab7037e528
1/* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#include <errno.h> 18#include <unistd.h> 19#include <stdio.h> 20#include <fcntl.h> 21 22#include <linux/fb.h> 23#include <sys/ioctl.h> 24#include <sys/mman.h> 25 26#include <binder/IMemory.h> 27#include <gui/SurfaceComposerClient.h> 28#include <gui/ISurfaceComposer.h> 29 30#include <SkImageEncoder.h> 31#include <SkBitmap.h> 32#include <SkData.h> 33#include <SkStream.h> 34 35using namespace android; 36 37static uint32_t DEFAULT_DISPLAY_ID = ISurfaceComposer::eDisplayIdMain; 38 39static void usage(const char* pname) 40{ 41 fprintf(stderr, 42 "usage: %s [-hp] [-d display-id] [FILENAME]\n" 43 " -h: this message\n" 44 " -p: save the file as a png.\n" 45 " -d: specify the display id to capture, default %d.\n" 46 "If FILENAME ends with .png it will be saved as a png.\n" 47 "If FILENAME is not given, the results will be printed to stdout.\n", 48 pname, DEFAULT_DISPLAY_ID 49 ); 50} 51 52static SkBitmap::Config flinger2skia(PixelFormat f) 53{ 54 switch (f) { 55 case PIXEL_FORMAT_A_8: 56 return SkBitmap::kA8_Config; 57 case PIXEL_FORMAT_RGB_565: 58 return SkBitmap::kRGB_565_Config; 59 case PIXEL_FORMAT_RGBA_4444: 60 return SkBitmap::kARGB_4444_Config; 61 default: 62 return SkBitmap::kARGB_8888_Config; 63 } 64} 65 66static status_t vinfoToPixelFormat(const fb_var_screeninfo& vinfo, 67 uint32_t* bytespp, uint32_t* f) 68{ 69 70 switch (vinfo.bits_per_pixel) { 71 case 16: 72 *f = PIXEL_FORMAT_RGB_565; 73 *bytespp = 2; 74 break; 75 case 24: 76 *f = PIXEL_FORMAT_RGB_888; 77 *bytespp = 3; 78 break; 79 case 32: 80 // TODO: do better decoding of vinfo here 81 *f = PIXEL_FORMAT_RGBX_8888; 82 *bytespp = 4; 83 break; 84 default: 85 return BAD_VALUE; 86 } 87 return NO_ERROR; 88} 89 90int main(int argc, char** argv) 91{ 92 const char* pname = argv[0]; 93 bool png = false; 94 int32_t displayId = DEFAULT_DISPLAY_ID; 95 int c; 96 while ((c = getopt(argc, argv, "phd:")) != -1) { 97 switch (c) { 98 case 'p': 99 png = true; 100 break; 101 case 'd': 102 displayId = atoi(optarg); 103 break; 104 case '?': 105 case 'h': 106 usage(pname); 107 return 1; 108 } 109 } 110 argc -= optind; 111 argv += optind; 112 113 int fd = -1; 114 if (argc == 0) { 115 fd = dup(STDOUT_FILENO); 116 } else if (argc == 1) { 117 const char* fn = argv[0]; 118 fd = open(fn, O_WRONLY | O_CREAT | O_TRUNC, 0664); 119 if (fd == -1) { 120 fprintf(stderr, "Error opening file: %s (%s)\n", fn, strerror(errno)); 121 return 1; 122 } 123 const int len = strlen(fn); 124 if (len >= 4 && 0 == strcmp(fn+len-4, ".png")) { 125 png = true; 126 } 127 } 128 129 if (fd == -1) { 130 usage(pname); 131 return 1; 132 } 133 134 void const* mapbase = MAP_FAILED; 135 ssize_t mapsize = -1; 136 137 void const* base = 0; 138 uint32_t w, h, f; 139 size_t size = 0; 140 141 ScreenshotClient screenshot; 142 sp<IBinder> display = SurfaceComposerClient::getBuiltInDisplay(displayId); 143 if (display != NULL && screenshot.update(display) == NO_ERROR) { 144 base = screenshot.getPixels(); 145 w = screenshot.getWidth(); 146 h = screenshot.getHeight(); 147 f = screenshot.getFormat(); 148 size = screenshot.getSize(); 149 } else { 150 const char* fbpath = "/dev/graphics/fb0"; 151 int fb = open(fbpath, O_RDONLY); 152 if (fb >= 0) { 153 struct fb_var_screeninfo vinfo; 154 if (ioctl(fb, FBIOGET_VSCREENINFO, &vinfo) == 0) { 155 uint32_t bytespp; 156 if (vinfoToPixelFormat(vinfo, &bytespp, &f) == NO_ERROR) { 157 size_t offset = (vinfo.xoffset + vinfo.yoffset*vinfo.xres) * bytespp; 158 w = vinfo.xres; 159 h = vinfo.yres; 160 size = w*h*bytespp; 161 mapsize = offset + size; 162 mapbase = mmap(0, mapsize, PROT_READ, MAP_PRIVATE, fb, 0); 163 if (mapbase != MAP_FAILED) { 164 base = (void const *)((char const *)mapbase + offset); 165 } 166 } 167 } 168 close(fb); 169 } 170 } 171 172 if (base) { 173 if (png) { 174 SkBitmap b; 175 b.setConfig(flinger2skia(f), w, h); 176 b.setPixels((void*)base); 177 SkDynamicMemoryWStream stream; 178 SkImageEncoder::EncodeStream(&stream, b, 179 SkImageEncoder::kPNG_Type, SkImageEncoder::kDefaultQuality); 180 SkData* streamData = stream.copyToData(); 181 write(fd, streamData->data(), streamData->size()); 182 streamData->unref(); 183 } else { 184 write(fd, &w, 4); 185 write(fd, &h, 4); 186 write(fd, &f, 4); 187 write(fd, base, size); 188 } 189 } 190 close(fd); 191 if (mapbase != MAP_FAILED) { 192 munmap((void *)mapbase, mapsize); 193 } 194 return 0; 195} 196