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 29#include <SkImageEncoder.h> 30#include <SkBitmap.h> 31#include <SkData.h> 32#include <SkStream.h> 33 34using namespace android; 35 36static void usage(const char* pname) 37{ 38 fprintf(stderr, 39 "usage: %s [-hp] [FILENAME]\n" 40 " -h: this message\n" 41 " -p: save the file as a png.\n" 42 "If FILENAME ends with .png it will be saved as a png.\n" 43 "If FILENAME is not given, the results will be printed to stdout.\n", 44 pname 45 ); 46} 47 48static SkBitmap::Config flinger2skia(PixelFormat f) 49{ 50 switch (f) { 51 case PIXEL_FORMAT_A_8: 52 return SkBitmap::kA8_Config; 53 case PIXEL_FORMAT_RGB_565: 54 return SkBitmap::kRGB_565_Config; 55 case PIXEL_FORMAT_RGBA_4444: 56 return SkBitmap::kARGB_4444_Config; 57 default: 58 return SkBitmap::kARGB_8888_Config; 59 } 60} 61 62static status_t vinfoToPixelFormat(const fb_var_screeninfo& vinfo, 63 uint32_t* bytespp, uint32_t* f) 64{ 65 66 switch (vinfo.bits_per_pixel) { 67 case 16: 68 *f = PIXEL_FORMAT_RGB_565; 69 *bytespp = 2; 70 break; 71 case 24: 72 *f = PIXEL_FORMAT_RGB_888; 73 *bytespp = 3; 74 break; 75 case 32: 76 // TODO: do better decoding of vinfo here 77 *f = PIXEL_FORMAT_RGBX_8888; 78 *bytespp = 4; 79 break; 80 default: 81 return BAD_VALUE; 82 } 83 return NO_ERROR; 84} 85 86int main(int argc, char** argv) 87{ 88 const char* pname = argv[0]; 89 bool png = false; 90 int c; 91 while ((c = getopt(argc, argv, "ph")) != -1) { 92 switch (c) { 93 case 'p': 94 png = true; 95 break; 96 case '?': 97 case 'h': 98 usage(pname); 99 return 1; 100 } 101 } 102 argc -= optind; 103 argv += optind; 104 105 int fd = -1; 106 if (argc == 0) { 107 fd = dup(STDOUT_FILENO); 108 } else if (argc == 1) { 109 const char* fn = argv[0]; 110 fd = open(fn, O_WRONLY | O_CREAT | O_TRUNC, 0664); 111 if (fd == -1) { 112 fprintf(stderr, "Error opening file: %s (%s)\n", fn, strerror(errno)); 113 return 1; 114 } 115 const int len = strlen(fn); 116 if (len >= 4 && 0 == strcmp(fn+len-4, ".png")) { 117 png = true; 118 } 119 } 120 121 if (fd == -1) { 122 usage(pname); 123 return 1; 124 } 125 126 void const* mapbase = MAP_FAILED; 127 ssize_t mapsize = -1; 128 129 void const* base = 0; 130 uint32_t w, h, f; 131 size_t size = 0; 132 133 ScreenshotClient screenshot; 134 if (screenshot.update() == NO_ERROR) { 135 base = screenshot.getPixels(); 136 w = screenshot.getWidth(); 137 h = screenshot.getHeight(); 138 f = screenshot.getFormat(); 139 size = screenshot.getSize(); 140 } else { 141 const char* fbpath = "/dev/graphics/fb0"; 142 int fb = open(fbpath, O_RDONLY); 143 if (fb >= 0) { 144 struct fb_var_screeninfo vinfo; 145 if (ioctl(fb, FBIOGET_VSCREENINFO, &vinfo) == 0) { 146 uint32_t bytespp; 147 if (vinfoToPixelFormat(vinfo, &bytespp, &f) == NO_ERROR) { 148 size_t offset = (vinfo.xoffset + vinfo.yoffset*vinfo.xres) * bytespp; 149 w = vinfo.xres; 150 h = vinfo.yres; 151 size = w*h*bytespp; 152 mapsize = offset + size; 153 mapbase = mmap(0, mapsize, PROT_READ, MAP_PRIVATE, fb, 0); 154 if (mapbase != MAP_FAILED) { 155 base = (void const *)((char const *)mapbase + offset); 156 } 157 } 158 } 159 close(fb); 160 } 161 } 162 163 if (base) { 164 if (png) { 165 SkBitmap b; 166 b.setConfig(flinger2skia(f), w, h); 167 b.setPixels((void*)base); 168 SkDynamicMemoryWStream stream; 169 SkImageEncoder::EncodeStream(&stream, b, 170 SkImageEncoder::kPNG_Type, SkImageEncoder::kDefaultQuality); 171 SkData* streamData = stream.copyToData(); 172 write(fd, streamData->data(), streamData->size()); 173 streamData->unref(); 174 } else { 175 write(fd, &w, 4); 176 write(fd, &h, 4); 177 write(fd, &f, 4); 178 write(fd, base, size); 179 } 180 } 181 close(fd); 182 if (mapbase != MAP_FAILED) { 183 munmap((void *)mapbase, mapsize); 184 } 185 return 0; 186} 187