blob: b615d443c8d1126eada0cdcf6704622e3bd65547 [file] [log] [blame]
joshualitt7f6a1e02016-01-22 11:21:43 -08001/*
2 * Copyright 2016 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "GrCaps.h"
9#include "GrContextFactory.h"
10#include "SkCanvas.h"
11#include "SkCommandLineFlags.h"
joshualitt609d9792016-01-27 11:07:23 -080012#include "SkJSONCanvas.h"
joshualitt9a4e1882016-01-27 07:03:29 -080013#include "SkPicture.h"
joshualitt7f6a1e02016-01-22 11:21:43 -080014#include "SkStream.h"
15#include "SkSurface.h"
16
joshualitt7f6a1e02016-01-22 11:21:43 -080017#include <sys/socket.h>
joshualitt7f6a1e02016-01-22 11:21:43 -080018#include <microhttpd.h>
19
20// To get image decoders linked in we have to do the below magic
21#include "SkForceLinking.h"
22#include "SkImageDecoder.h"
23__SK_FORCE_IMAGE_DECODER_LINKING;
24
25// TODO make this configurable
26#define PORT 8888
27
28DEFINE_string(dir, "skps", "Directory to read skp.");
29DEFINE_string(name, "desk_carsvg", "skp to load.");
30DEFINE_bool(useTemplate, true, "whether or not to use the skdebugger template string.");
31
32// TODO probably want to make this configurable
33static const int kImageWidth = 1920;
34static const int kImageHeight = 1080;
35
joshualitt7f6a1e02016-01-22 11:21:43 -080036// TODO move to template file
37SkString generateTemplate(SkString source) {
38 SkString debuggerTemplate;
39 debuggerTemplate.appendf(
40 "<!DOCTYPE html>\n"
41 "<html>\n"
42 "<head>\n"
43 " <title>SkDebugger</title>\n"
44 " <meta charset=\"utf-8\" />\n"
45 " <meta http-equiv=\"X-UA-Compatible\" content=\"IE=egde,chrome=1\">\n"
46 " <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n"
47 " <script src=\"%s/res/js/core.js\" type=\"text/javascript\" charset=\"utf-8\"></script>\n"
48 " <link href=\"%s/res/vul/elements.html\" rel=\"import\" />\n"
49 "</head>\n"
50 "<body class=\"fullbleed layout vertical\">\n"
51 " <debugger-app-sk>This is the app."
52 " </debugger-app-sk>\n"
53 "</body>\n"
54 "</html>", source.c_str(), source.c_str());
55 return debuggerTemplate;
56
57}
58
joshualitt9a4e1882016-01-27 07:03:29 -080059struct UploadContext {
joshualitt609d9792016-01-27 11:07:23 -080060 SkDynamicMemoryWStream fStream;
61 MHD_PostProcessor* fPostProcessor;
joshualitt9a4e1882016-01-27 07:03:29 -080062 MHD_Connection* connection;
63};
64
65struct Request {
66 Request() : fUploadContext(nullptr) {}
67 UploadContext* fUploadContext;
68 SkAutoTUnref<SkData> fPNG;
joshualitt609d9792016-01-27 11:07:23 -080069 SkAutoTUnref<SkPicture> fPicture;
joshualitt9a4e1882016-01-27 07:03:29 -080070};
71
joshualitt609d9792016-01-27 11:07:23 -080072// TODO factor this out into functions, also handle CPU path
73bool setupAndDrawToCanvas(Request* request, SkString* error) {
74 GrContextOptions grContextOpts;
75 SkAutoTDelete<GrContextFactory> factory(new GrContextFactory(grContextOpts));
76
77 GrContext* context = factory->get(GrContextFactory::kNative_GLContextType,
78 GrContextFactory::kNone_GLContextOptions);
79 int maxRTSize = context->caps()->maxRenderTargetSize();
80 SkImageInfo info = SkImageInfo::Make(SkTMin(kImageWidth, maxRTSize),
81 SkTMin(kImageHeight, maxRTSize),
82 kN32_SkColorType, kPremul_SkAlphaType);
83 uint32_t flags = 0;
84 SkSurfaceProps props(flags, SkSurfaceProps::kLegacyFontHost_InitType);
85 SkAutoTUnref<SkSurface> surface(SkSurface::NewRenderTarget(context,
86 SkSurface::kNo_Budgeted, info,
87 0, &props));
88 SkASSERT(surface.get());
89
90 SkGLContext* gl = factory->getContextInfo(GrContextFactory::kNative_GLContextType,
91 GrContextFactory::kNone_GLContextOptions).fGLContext;
92 gl->makeCurrent();
93
94 // draw
95 request->fPicture.reset(
96 SkPicture::CreateFromStream(request->fUploadContext->fStream.detachAsStream()));
97 if (!request->fPicture.get()) {
98 error->appendf("Could not create picture from stream.\n");
99 return false;
100 }
101
102 SkCanvas* canvas = surface->getCanvas();
103 canvas->drawPicture(request->fPicture);
104
105 // capture pixels
106 SkBitmap bmp;
107 bmp.setInfo(canvas->imageInfo());
108 if (!canvas->readPixels(&bmp, 0, 0)) {
109 error->appendf("Can't read canvas pixels.\n");
110 return false;
111 }
112
113 // write to png
114 request->fPNG.reset(SkImageEncoder::EncodeData(bmp, SkImageEncoder::kPNG_Type, 100));
115 if (!request->fPNG) {
116 error->appendf("Can't encode a PNG.\n");
117 return false;
118 }
119 return true;
120}
121
joshualitt9a4e1882016-01-27 07:03:29 -0800122static const size_t kBufferSize = 1024;
123
124static int process_upload_data(void* cls, enum MHD_ValueKind kind,
125 const char* key, const char* filename,
126 const char* content_type, const char* transfer_encoding,
127 const char* data, uint64_t off, size_t size) {
128 struct UploadContext* uc = reinterpret_cast<UploadContext*>(cls);
129
130 if (0 != size) {
joshualitt609d9792016-01-27 11:07:23 -0800131 uc->fStream.write(data, size);
joshualitt9a4e1882016-01-27 07:03:29 -0800132 }
133 return MHD_YES;
134}
135
joshualitt609d9792016-01-27 11:07:23 -0800136static int SendData(MHD_Connection* connection, const SkData* data, const char* type) {
joshualittccfdaa52016-01-27 07:40:29 -0800137 MHD_Response* response = MHD_create_response_from_buffer(data->size(),
138 const_cast<void*>(data->data()),
139 MHD_RESPMEM_MUST_COPY);
joshualitt609d9792016-01-27 11:07:23 -0800140 MHD_add_response_header(response, "Content-Type", type);
joshualittccfdaa52016-01-27 07:40:29 -0800141
142 int ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
143 MHD_destroy_response(response);
144 return ret;
145}
146
joshualitt609d9792016-01-27 11:07:23 -0800147static int SendJSON(MHD_Connection* connection, SkPicture* picture) {
148 SkDynamicMemoryWStream stream;
149 SkAutoTUnref<SkJSONCanvas> jsonCanvas(new SkJSONCanvas(kImageWidth, kImageHeight, stream));
150 jsonCanvas->drawPicture(picture);
151 jsonCanvas->finish();
152
153 SkAutoTUnref<SkData> data(stream.copyToData());
154 return SendData(connection, data, "application/json");
155}
156
157static int SendTemplate(MHD_Connection* connection, bool redirect = false,
158 const char* redirectUrl = nullptr) {
joshualittccfdaa52016-01-27 07:40:29 -0800159 SkString debuggerTemplate = generateTemplate(SkString("http://debugger.skia.org"));
160
161 MHD_Response* response = MHD_create_response_from_buffer(
162 debuggerTemplate.size(),
163 (void*) const_cast<char*>(debuggerTemplate.c_str()),
164 MHD_RESPMEM_MUST_COPY);
165
jcgregorio6f17bc52016-01-27 11:44:38 -0800166 int status = MHD_HTTP_OK;
167
joshualitt609d9792016-01-27 11:07:23 -0800168 if (redirect) {
169 MHD_add_response_header (response, "Location", redirectUrl);
jcgregorio6f17bc52016-01-27 11:44:38 -0800170 status = MHD_HTTP_SEE_OTHER;
joshualitt609d9792016-01-27 11:07:23 -0800171 }
172
jcgregorio6f17bc52016-01-27 11:44:38 -0800173 int ret = MHD_queue_response(connection, status, response);
joshualittccfdaa52016-01-27 07:40:29 -0800174 MHD_destroy_response(response);
175 return ret;
176}
177
178typedef int (*UrlHandler)(Request* request, MHD_Connection* connection,
179 const char* upload_data, size_t* upload_data_size);
180
181int rootHandler(Request* request, MHD_Connection* connection,
182 const char* upload_data, size_t* upload_data_size) {
183 return SendTemplate(connection);
184}
185
186int postHandler(Request* request, MHD_Connection* connection,
187 const char* upload_data, size_t* upload_data_size) {
188 UploadContext* uc = request->fUploadContext;
189
190 // New connection
191 if (!uc) {
192 // TODO make this a method on request
193 uc = new UploadContext;
194 uc->connection = connection;
joshualitt609d9792016-01-27 11:07:23 -0800195 uc->fPostProcessor = MHD_create_post_processor(connection, kBufferSize,
196 &process_upload_data, uc);
197 SkASSERT(uc->fPostProcessor);
joshualittccfdaa52016-01-27 07:40:29 -0800198
199 request->fUploadContext = uc;
200 return MHD_YES;
201 }
202
203 // in process upload
204 if (0 != *upload_data_size) {
joshualitt609d9792016-01-27 11:07:23 -0800205 SkASSERT(uc->fPostProcessor);
206 MHD_post_process(uc->fPostProcessor, upload_data, *upload_data_size);
joshualittccfdaa52016-01-27 07:40:29 -0800207 *upload_data_size = 0;
208 return MHD_YES;
209 }
210
211 // end of upload
joshualitt609d9792016-01-27 11:07:23 -0800212 MHD_destroy_post_processor(uc->fPostProcessor);
213 uc->fPostProcessor = nullptr;
joshualittccfdaa52016-01-27 07:40:29 -0800214
215 // TODO response
216 SkString error;
joshualitt609d9792016-01-27 11:07:23 -0800217 if (!setupAndDrawToCanvas(request, &error)) {
joshualittccfdaa52016-01-27 07:40:29 -0800218 // TODO send error
219 return MHD_YES;
220 }
221
joshualitt609d9792016-01-27 11:07:23 -0800222 return SendTemplate(connection, true, "/");
joshualittccfdaa52016-01-27 07:40:29 -0800223}
224
225int imgHandler(Request* request, MHD_Connection* connection,
226 const char* upload_data, size_t* upload_data_size) {
227 if (request->fPNG.get()) {
228 SkData* data = request->fPNG.get();
joshualitt609d9792016-01-27 11:07:23 -0800229 return SendData(connection, data, "image/png");
230 }
231 return MHD_NO;
232}
233
234int infoHandler(Request* request, MHD_Connection* connection,
235 const char* upload_data, size_t* upload_data_size) {
236 if (request->fPicture.get()) {
237 return SendJSON(connection, request->fPicture);
joshualittccfdaa52016-01-27 07:40:29 -0800238 }
239 return MHD_NO;
240}
241
242class UrlManager {
243public:
244 UrlManager() {
245 // Register handlers
246 fHandlers.push_back({MHD_HTTP_METHOD_GET, "/", rootHandler});
247 fHandlers.push_back({MHD_HTTP_METHOD_POST, "/new", postHandler});
248 fHandlers.push_back({MHD_HTTP_METHOD_GET, "/img", imgHandler});
jcgregorio9ce41102016-01-27 12:15:38 -0800249 fHandlers.push_back({MHD_HTTP_METHOD_GET, "/cmd", infoHandler});
joshualittccfdaa52016-01-27 07:40:29 -0800250 }
251
252 // This is clearly not efficient for a large number of urls and handlers
253 int invoke(Request* request, MHD_Connection* connection, const char* url, const char* method,
254 const char* upload_data, size_t* upload_data_size) const {
255 for (int i = 0; i < fHandlers.count(); i++) {
256 const Url& urlHandler = fHandlers[i];
257 if (0 == strcmp(method, urlHandler.fMethod) &&
258 0 == strcmp(url, urlHandler.fPath)) {
259 return (*urlHandler.fHandler)(request, connection, upload_data,
260 upload_data_size);
261 }
262 }
263 return MHD_NO;
264 }
265
266private:
267 struct Url {
268 const char* fMethod;
269 const char* fPath;
270 UrlHandler fHandler;
271 };
272 SkTArray<Url> fHandlers;
273};
274
275const UrlManager kUrlManager;
276
joshualitt7f6a1e02016-01-22 11:21:43 -0800277int answer_to_connection(void* cls, struct MHD_Connection* connection,
278 const char* url, const char* method, const char* version,
279 const char* upload_data, size_t* upload_data_size,
280 void** con_cls) {
joshualitt9a4e1882016-01-27 07:03:29 -0800281 SkDebugf("New %s request for %s using version %s\n", method, url, version);
joshualitt7f6a1e02016-01-22 11:21:43 -0800282
joshualitt9a4e1882016-01-27 07:03:29 -0800283 Request* request = reinterpret_cast<Request*>(cls);
joshualittccfdaa52016-01-27 07:40:29 -0800284 return kUrlManager.invoke(request, connection, url, method, upload_data, upload_data_size);
joshualitt7f6a1e02016-01-22 11:21:43 -0800285}
286
287int skiaserve_main() {
joshualitt9a4e1882016-01-27 07:03:29 -0800288 Request request; // This simple server has one request
joshualitt7f6a1e02016-01-22 11:21:43 -0800289 struct MHD_Daemon* daemon;
joshualitt9a4e1882016-01-27 07:03:29 -0800290 daemon = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, PORT, nullptr, nullptr,
291 &answer_to_connection, &request,
292 MHD_OPTION_END);
joshualitt7f6a1e02016-01-22 11:21:43 -0800293 if (NULL == daemon) {
294 return 1;
295 }
296
297 getchar();
298 MHD_stop_daemon(daemon);
299 return 0;
300}
301
302#if !defined SK_BUILD_FOR_IOS
303int main(int argc, char** argv) {
304 SkCommandLineFlags::Parse(argc, argv);
305 return skiaserve_main();
306}
307#endif