blob: 2b365d837805e2bfc6221fbde4973676655e7641 [file] [log] [blame]
Mike Lockwoodc59b2f92012-10-24 12:31:10 -07001/*
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>
Umair Khancfed2322014-01-15 08:08:50 -050021#include <stdlib.h>
22#include <string.h>
Mike Lockwoodc59b2f92012-10-24 12:31:10 -070023
24#include <linux/fb.h>
25#include <sys/ioctl.h>
26#include <sys/mman.h>
27
Mathias Agopian0678a8c2013-03-19 20:56:00 -070028#include <binder/ProcessState.h>
29
Mike Lockwoodc59b2f92012-10-24 12:31:10 -070030#include <gui/SurfaceComposerClient.h>
31#include <gui/ISurfaceComposer.h>
32
Mathias Agopian0137fb82013-03-20 15:38:07 -070033#include <ui/PixelFormat.h>
34
Mike Lockwoodc59b2f92012-10-24 12:31:10 -070035#include <SkImageEncoder.h>
36#include <SkBitmap.h>
37#include <SkData.h>
38#include <SkStream.h>
39
40using namespace android;
41
42static uint32_t DEFAULT_DISPLAY_ID = ISurfaceComposer::eDisplayIdMain;
43
44static void usage(const char* pname)
45{
46 fprintf(stderr,
47 "usage: %s [-hp] [-d display-id] [FILENAME]\n"
48 " -h: this message\n"
49 " -p: save the file as a png.\n"
50 " -d: specify the display id to capture, default %d.\n"
51 "If FILENAME ends with .png it will be saved as a png.\n"
52 "If FILENAME is not given, the results will be printed to stdout.\n",
53 pname, DEFAULT_DISPLAY_ID
54 );
55}
56
57static SkBitmap::Config flinger2skia(PixelFormat f)
58{
59 switch (f) {
Mike Lockwoodc59b2f92012-10-24 12:31:10 -070060 case PIXEL_FORMAT_RGB_565:
61 return SkBitmap::kRGB_565_Config;
Mike Lockwoodc59b2f92012-10-24 12:31:10 -070062 default:
63 return SkBitmap::kARGB_8888_Config;
64 }
65}
66
67static status_t vinfoToPixelFormat(const fb_var_screeninfo& vinfo,
68 uint32_t* bytespp, uint32_t* f)
69{
70
71 switch (vinfo.bits_per_pixel) {
72 case 16:
73 *f = PIXEL_FORMAT_RGB_565;
74 *bytespp = 2;
75 break;
76 case 24:
77 *f = PIXEL_FORMAT_RGB_888;
78 *bytespp = 3;
79 break;
80 case 32:
81 // TODO: do better decoding of vinfo here
82 *f = PIXEL_FORMAT_RGBX_8888;
83 *bytespp = 4;
84 break;
85 default:
86 return BAD_VALUE;
87 }
88 return NO_ERROR;
89}
90
Umair Khancfed2322014-01-15 08:08:50 -050091static status_t notifyMediaScanner(const char* fileName) {
92 String8 cmd("am broadcast -a android.intent.action.MEDIA_SCANNER_SCAN_FILE -d file://");
93 String8 fileUrl("\"");
94 fileUrl.append(fileName);
95 fileUrl.append("\"");
96 cmd.append(fileName);
97 cmd.append(" > /dev/null");
98 int result = system(cmd.string());
99 if (result < 0) {
100 fprintf(stderr, "Unable to broadcast intent for media scanner.\n");
101 return UNKNOWN_ERROR;
102 }
103 return NO_ERROR;
104}
105
Mike Lockwoodc59b2f92012-10-24 12:31:10 -0700106int main(int argc, char** argv)
107{
Mathias Agopian0678a8c2013-03-19 20:56:00 -0700108 ProcessState::self()->startThreadPool();
109
Mike Lockwoodc59b2f92012-10-24 12:31:10 -0700110 const char* pname = argv[0];
111 bool png = false;
112 int32_t displayId = DEFAULT_DISPLAY_ID;
113 int c;
114 while ((c = getopt(argc, argv, "phd:")) != -1) {
115 switch (c) {
116 case 'p':
117 png = true;
118 break;
119 case 'd':
120 displayId = atoi(optarg);
121 break;
122 case '?':
123 case 'h':
124 usage(pname);
125 return 1;
126 }
127 }
128 argc -= optind;
129 argv += optind;
130
131 int fd = -1;
Umair Khancfed2322014-01-15 08:08:50 -0500132 const char* fn;
Mike Lockwoodc59b2f92012-10-24 12:31:10 -0700133 if (argc == 0) {
134 fd = dup(STDOUT_FILENO);
135 } else if (argc == 1) {
Umair Khancfed2322014-01-15 08:08:50 -0500136 fn = argv[0];
Mike Lockwoodc59b2f92012-10-24 12:31:10 -0700137 fd = open(fn, O_WRONLY | O_CREAT | O_TRUNC, 0664);
138 if (fd == -1) {
139 fprintf(stderr, "Error opening file: %s (%s)\n", fn, strerror(errno));
140 return 1;
141 }
142 const int len = strlen(fn);
143 if (len >= 4 && 0 == strcmp(fn+len-4, ".png")) {
144 png = true;
145 }
146 }
147
148 if (fd == -1) {
149 usage(pname);
150 return 1;
151 }
152
153 void const* mapbase = MAP_FAILED;
154 ssize_t mapsize = -1;
155
156 void const* base = 0;
Mathias Agopian0137fb82013-03-20 15:38:07 -0700157 uint32_t w, s, h, f;
Mike Lockwoodc59b2f92012-10-24 12:31:10 -0700158 size_t size = 0;
159
160 ScreenshotClient screenshot;
161 sp<IBinder> display = SurfaceComposerClient::getBuiltInDisplay(displayId);
162 if (display != NULL && screenshot.update(display) == NO_ERROR) {
163 base = screenshot.getPixels();
164 w = screenshot.getWidth();
165 h = screenshot.getHeight();
Mathias Agopian0137fb82013-03-20 15:38:07 -0700166 s = screenshot.getStride();
Mike Lockwoodc59b2f92012-10-24 12:31:10 -0700167 f = screenshot.getFormat();
168 size = screenshot.getSize();
169 } else {
170 const char* fbpath = "/dev/graphics/fb0";
171 int fb = open(fbpath, O_RDONLY);
172 if (fb >= 0) {
173 struct fb_var_screeninfo vinfo;
174 if (ioctl(fb, FBIOGET_VSCREENINFO, &vinfo) == 0) {
175 uint32_t bytespp;
176 if (vinfoToPixelFormat(vinfo, &bytespp, &f) == NO_ERROR) {
177 size_t offset = (vinfo.xoffset + vinfo.yoffset*vinfo.xres) * bytespp;
178 w = vinfo.xres;
179 h = vinfo.yres;
Mathias Agopian0137fb82013-03-20 15:38:07 -0700180 s = vinfo.xres;
Mike Lockwoodc59b2f92012-10-24 12:31:10 -0700181 size = w*h*bytespp;
182 mapsize = offset + size;
183 mapbase = mmap(0, mapsize, PROT_READ, MAP_PRIVATE, fb, 0);
184 if (mapbase != MAP_FAILED) {
185 base = (void const *)((char const *)mapbase + offset);
186 }
187 }
188 }
189 close(fb);
190 }
191 }
192
193 if (base) {
194 if (png) {
195 SkBitmap b;
Mathias Agopiana79fd4c2013-03-21 21:44:49 -0700196 b.setConfig(flinger2skia(f), w, h, s*bytesPerPixel(f));
Mike Lockwoodc59b2f92012-10-24 12:31:10 -0700197 b.setPixels((void*)base);
198 SkDynamicMemoryWStream stream;
199 SkImageEncoder::EncodeStream(&stream, b,
200 SkImageEncoder::kPNG_Type, SkImageEncoder::kDefaultQuality);
201 SkData* streamData = stream.copyToData();
202 write(fd, streamData->data(), streamData->size());
203 streamData->unref();
Umair Khancfed2322014-01-15 08:08:50 -0500204 notifyMediaScanner(fn);
Mike Lockwoodc59b2f92012-10-24 12:31:10 -0700205 } else {
206 write(fd, &w, 4);
207 write(fd, &h, 4);
208 write(fd, &f, 4);
Mathias Agopian0137fb82013-03-20 15:38:07 -0700209 size_t Bpp = bytesPerPixel(f);
210 for (size_t y=0 ; y<h ; y++) {
211 write(fd, base, w*Bpp);
212 base = (void *)((char *)base + s*Bpp);
213 }
Mike Lockwoodc59b2f92012-10-24 12:31:10 -0700214 }
215 }
216 close(fd);
217 if (mapbase != MAP_FAILED) {
218 munmap((void *)mapbase, mapsize);
219 }
220 return 0;
221}