blob: 96760b91aaba52db157aaeefd63211de3cda4876 [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"
joshualitt136f5172016-02-02 11:07:39 -080012#include "SkDebugCanvas.h"
joshualitt609d9792016-01-27 11:07:23 -080013#include "SkJSONCanvas.h"
joshualitt29e5a892016-02-04 06:08:33 -080014#include "SkJSONCPP.h"
joshualitt9a4e1882016-01-27 07:03:29 -080015#include "SkPicture.h"
joshualitt792345f2016-02-02 13:02:33 -080016#include "SkPictureRecorder.h"
17#include "SkPixelSerializer.h"
joshualitt7f6a1e02016-01-22 11:21:43 -080018#include "SkStream.h"
19#include "SkSurface.h"
20
joshualittcdad12f2016-02-08 07:08:21 -080021#include "UrlDataManager.h"
22
joshualitt7f6a1e02016-01-22 11:21:43 -080023#include <sys/socket.h>
joshualitt7f6a1e02016-01-22 11:21:43 -080024#include <microhttpd.h>
ethannicholas546d6652016-02-16 11:03:04 -080025#include "png.h"
joshualitt7f6a1e02016-01-22 11:21:43 -080026
27// To get image decoders linked in we have to do the below magic
28#include "SkForceLinking.h"
29#include "SkImageDecoder.h"
30__SK_FORCE_IMAGE_DECODER_LINKING;
31
jcgregorio21ab1202016-01-28 06:24:19 -080032DEFINE_string(source, "https://debugger.skia.org", "Where to load the web UI from.");
33DEFINE_int32(port, 8888, "The port to listen on.");
joshualitt7f6a1e02016-01-22 11:21:43 -080034
35// TODO probably want to make this configurable
36static const int kImageWidth = 1920;
37static const int kImageHeight = 1080;
38
joshualitt7f6a1e02016-01-22 11:21:43 -080039SkString generateTemplate(SkString source) {
40 SkString debuggerTemplate;
41 debuggerTemplate.appendf(
42 "<!DOCTYPE html>\n"
43 "<html>\n"
44 "<head>\n"
45 " <title>SkDebugger</title>\n"
46 " <meta charset=\"utf-8\" />\n"
47 " <meta http-equiv=\"X-UA-Compatible\" content=\"IE=egde,chrome=1\">\n"
48 " <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n"
49 " <script src=\"%s/res/js/core.js\" type=\"text/javascript\" charset=\"utf-8\"></script>\n"
jcgregorio50fe38e2016-02-25 07:02:13 -080050 " <link href=\"%s/res/vul/elements.html\" rel=\"import\" />\n"
51 " <link rel='shortcut icon' href='https://debugger.skia.org/res/img/favicon.ico' type='image/x-icon'/ >"
joshualitt7f6a1e02016-01-22 11:21:43 -080052 "</head>\n"
53 "<body class=\"fullbleed layout vertical\">\n"
54 " <debugger-app-sk>This is the app."
55 " </debugger-app-sk>\n"
56 "</body>\n"
57 "</html>", source.c_str(), source.c_str());
58 return debuggerTemplate;
59
60}
61
joshualitt9a4e1882016-01-27 07:03:29 -080062struct UploadContext {
joshualitt609d9792016-01-27 11:07:23 -080063 SkDynamicMemoryWStream fStream;
64 MHD_PostProcessor* fPostProcessor;
joshualitt9a4e1882016-01-27 07:03:29 -080065 MHD_Connection* connection;
66};
67
68struct Request {
jcgregorio50fe38e2016-02-25 07:02:13 -080069 Request(SkString rootUrl)
ethannicholas3ff5d8c2016-02-22 08:59:57 -080070 : fUploadContext(nullptr)
71 , fUrlDataManager(rootUrl)
72 , fGPUEnabled(false) {}
73
joshualitt9a4e1882016-01-27 07:03:29 -080074 UploadContext* fUploadContext;
joshualitt609d9792016-01-27 11:07:23 -080075 SkAutoTUnref<SkPicture> fPicture;
joshualitt136f5172016-02-02 11:07:39 -080076 SkAutoTUnref<SkDebugCanvas> fDebugCanvas;
ethannicholasde8e54c2016-02-12 10:09:34 -080077 SkAutoTDelete<GrContextFactory> fContextFactory;
ethannicholas272af6e2016-02-17 10:30:27 -080078 SkAutoTUnref<SkSurface> fSurface;
joshualittcdad12f2016-02-08 07:08:21 -080079 UrlDataManager fUrlDataManager;
ethannicholas3ff5d8c2016-02-22 08:59:57 -080080 bool fGPUEnabled;
joshualitt9a4e1882016-01-27 07:03:29 -080081};
82
ethannicholas546d6652016-02-16 11:03:04 -080083static void write_png_callback(png_structp png_ptr, png_bytep data, png_size_t length) {
84 SkWStream* out = (SkWStream*) png_get_io_ptr(png_ptr);
85 out->write(data, length);
86}
87
88static void write_png(const png_bytep rgba, png_uint_32 width, png_uint_32 height, SkWStream& out) {
89 png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
90 SkASSERT(png != nullptr);
91 png_infop info_ptr = png_create_info_struct(png);
92 SkASSERT(info_ptr != nullptr);
93 if (setjmp(png_jmpbuf(png))) {
94 SkFAIL("png encode error");
95 }
ethannicholascecbbe22016-02-17 11:49:43 -080096 png_set_IHDR(png, info_ptr, width, height, 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE,
ethannicholas546d6652016-02-16 11:03:04 -080097 PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
98 png_set_compression_level(png, 1);
99 png_bytepp rows = (png_bytepp) sk_malloc_throw(height * sizeof(png_byte*));
ethannicholascecbbe22016-02-17 11:49:43 -0800100 png_bytep pixels = (png_bytep) sk_malloc_throw(width * height * 3);
ethannicholas546d6652016-02-16 11:03:04 -0800101 for (png_size_t y = 0; y < height; ++y) {
ethannicholascecbbe22016-02-17 11:49:43 -0800102 const png_bytep src = rgba + y * width * 4;
103 rows[y] = pixels + y * width * 3;
104 // convert from RGBA to RGB
105 for (png_size_t x = 0; x < width; ++x) {
106 rows[y][x * 3] = src[x * 4];
107 rows[y][x * 3 + 1] = src[x * 4 + 1];
108 rows[y][x * 3 + 2] = src[x * 4 + 2];
109 }
ethannicholas546d6652016-02-16 11:03:04 -0800110 }
111 png_set_filter(png, 0, PNG_NO_FILTERS);
112 png_set_rows(png, info_ptr, &rows[0]);
113 png_set_write_fn(png, &out, write_png_callback, NULL);
114 png_write_png(png, info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
115 png_destroy_write_struct(&png, NULL);
116 sk_free(rows);
117}
118
ethannicholas3ca1e1a2016-02-18 10:22:34 -0800119SkBitmap* getBitmapFromCanvas(SkCanvas* canvas) {
120 SkBitmap* bmp = new SkBitmap();
121 SkImageInfo info = SkImageInfo::Make(kImageWidth, kImageHeight, kRGBA_8888_SkColorType,
122 kOpaque_SkAlphaType);
123 bmp->setInfo(info);
124 if (!canvas->readPixels(bmp, 0, 0)) {
ethannicholas272af6e2016-02-17 10:30:27 -0800125 fprintf(stderr, "Can't read pixels\n");
126 return nullptr;
127 }
ethannicholas3ca1e1a2016-02-18 10:22:34 -0800128 return bmp;
129}
130
131SkData* writeCanvasToPng(SkCanvas* canvas) {
132 // capture pixels
133 SkAutoTDelete<SkBitmap> bmp(getBitmapFromCanvas(canvas));
134 SkASSERT(bmp);
ethannicholas272af6e2016-02-17 10:30:27 -0800135
joshualitt609d9792016-01-27 11:07:23 -0800136 // write to png
ethannicholas546d6652016-02-16 11:03:04 -0800137 SkDynamicMemoryWStream buffer;
ethannicholas3ca1e1a2016-02-18 10:22:34 -0800138 write_png((const png_bytep) bmp->getPixels(), bmp->width(), bmp->height(), buffer);
ethannicholas546d6652016-02-16 11:03:04 -0800139 return buffer.copyToData();
joshualitt136f5172016-02-02 11:07:39 -0800140}
141
joshualitt6b3cf732016-02-17 11:20:26 -0800142SkCanvas* getCanvasFromRequest(Request* request) {
ethannicholasde8e54c2016-02-12 10:09:34 -0800143 GrContextFactory* factory = request->fContextFactory;
144 SkGLContext* gl = factory->getContextInfo(GrContextFactory::kNative_GLContextType,
145 GrContextFactory::kNone_GLContextOptions).fGLContext;
146 gl->makeCurrent();
147 SkASSERT(request->fDebugCanvas);
ethannicholas272af6e2016-02-17 10:30:27 -0800148 SkCanvas* target = request->fSurface->getCanvas();
joshualitt6b3cf732016-02-17 11:20:26 -0800149 return target;
150}
151
ethannicholas3ca1e1a2016-02-18 10:22:34 -0800152void drawToCanvas(Request* request, int n) {
joshualitt6b3cf732016-02-17 11:20:26 -0800153 SkCanvas* target = getCanvasFromRequest(request);
ethannicholas272af6e2016-02-17 10:30:27 -0800154 request->fDebugCanvas->drawTo(target, n);
ethannicholas3ca1e1a2016-02-18 10:22:34 -0800155}
156
157SkData* drawToPng(Request* request, int n) {
158 drawToCanvas(request, n);
159 return writeCanvasToPng(getCanvasFromRequest(request));
joshualitt609d9792016-01-27 11:07:23 -0800160}
161
ethannicholas85fca852016-02-19 08:40:59 -0800162SkSurface* createCPUSurface() {
joshualitt29e5a892016-02-04 06:08:33 -0800163 SkImageInfo info = SkImageInfo::Make(kImageWidth, kImageHeight, kN32_SkColorType,
164 kPremul_SkAlphaType);
165 return SkSurface::NewRaster(info);
166}
167
ethannicholas85fca852016-02-19 08:40:59 -0800168SkSurface* createGPUSurface(Request* request) {
169 GrContext* context = request->fContextFactory->get(GrContextFactory::kNative_GLContextType,
170 GrContextFactory::kNone_GLContextOptions);
171 int maxRTSize = context->caps()->maxRenderTargetSize();
172 SkImageInfo info = SkImageInfo::Make(SkTMin(kImageWidth, maxRTSize),
173 SkTMin(kImageHeight, maxRTSize),
174 kN32_SkColorType, kPremul_SkAlphaType);
175 uint32_t flags = 0;
176 SkSurfaceProps props(flags, SkSurfaceProps::kLegacyFontHost_InitType);
bsalomon5ec26ae2016-02-25 08:33:02 -0800177 SkSurface* surface = SkSurface::NewRenderTarget(context, SkBudgeted::kNo, info, 0,
ethannicholas85fca852016-02-19 08:40:59 -0800178 &props);
179 return surface;
180}
181
joshualitt9a4e1882016-01-27 07:03:29 -0800182static const size_t kBufferSize = 1024;
183
184static int process_upload_data(void* cls, enum MHD_ValueKind kind,
185 const char* key, const char* filename,
186 const char* content_type, const char* transfer_encoding,
187 const char* data, uint64_t off, size_t size) {
188 struct UploadContext* uc = reinterpret_cast<UploadContext*>(cls);
189
190 if (0 != size) {
joshualitt609d9792016-01-27 11:07:23 -0800191 uc->fStream.write(data, size);
joshualitt9a4e1882016-01-27 07:03:29 -0800192 }
193 return MHD_YES;
194}
195
jcgregorio12d47ce2016-02-10 14:10:37 -0800196// SendOK just sends an empty response with a 200 OK status code.
197static int SendOK(MHD_Connection* connection) {
198 const char* data = "";
199
200 MHD_Response* response = MHD_create_response_from_buffer(strlen(data),
201 (void*)data,
202 MHD_RESPMEM_PERSISTENT);
203 int ret = MHD_queue_response(connection, 200, response);
204 MHD_destroy_response(response);
205 return ret;
206}
207
ethannicholas85fca852016-02-19 08:40:59 -0800208static int SendError(MHD_Connection* connection, const char* msg) {
209 MHD_Response* response = MHD_create_response_from_buffer(strlen(msg),
210 (void*) msg,
211 MHD_RESPMEM_PERSISTENT);
212 int ret = MHD_queue_response(connection, 500, response);
213 MHD_destroy_response(response);
214 return ret;
215}
216
joshualitt792345f2016-02-02 13:02:33 -0800217static int SendData(MHD_Connection* connection, const SkData* data, const char* type,
218 bool setContentDisposition = false, const char* dispositionString = nullptr) {
joshualittccfdaa52016-01-27 07:40:29 -0800219 MHD_Response* response = MHD_create_response_from_buffer(data->size(),
220 const_cast<void*>(data->data()),
221 MHD_RESPMEM_MUST_COPY);
joshualitt609d9792016-01-27 11:07:23 -0800222 MHD_add_response_header(response, "Content-Type", type);
joshualittccfdaa52016-01-27 07:40:29 -0800223
joshualitt792345f2016-02-02 13:02:33 -0800224 if (setContentDisposition) {
225 MHD_add_response_header(response, "Content-Disposition", dispositionString);
226 }
227
joshualittccfdaa52016-01-27 07:40:29 -0800228 int ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
229 MHD_destroy_response(response);
230 return ret;
231}
232
ethannicholas3ff5d8c2016-02-22 08:59:57 -0800233static int SendJSON(MHD_Connection* connection, Request* request, int n) {
234 SkCanvas* canvas = getCanvasFromRequest(request);
235 SkDebugCanvas* debugCanvas = request->fDebugCanvas;
236 UrlDataManager* urlDataManager = &request->fUrlDataManager;
joshualitt6b3cf732016-02-17 11:20:26 -0800237 Json::Value root = debugCanvas->toJSON(*urlDataManager, n, canvas);
ethannicholas3ff5d8c2016-02-22 08:59:57 -0800238 root["mode"] = Json::Value(request->fGPUEnabled ? "gpu" : "cpu");
joshualitt609d9792016-01-27 11:07:23 -0800239 SkDynamicMemoryWStream stream;
joshualittdb6a2542016-02-11 07:09:51 -0800240 stream.writeText(Json::FastWriter().write(root).c_str());
joshualitt609d9792016-01-27 11:07:23 -0800241
242 SkAutoTUnref<SkData> data(stream.copyToData());
243 return SendData(connection, data, "application/json");
244}
245
246static int SendTemplate(MHD_Connection* connection, bool redirect = false,
247 const char* redirectUrl = nullptr) {
jcgregorio21ab1202016-01-28 06:24:19 -0800248 SkString debuggerTemplate = generateTemplate(SkString(FLAGS_source[0]));
joshualittccfdaa52016-01-27 07:40:29 -0800249
250 MHD_Response* response = MHD_create_response_from_buffer(
251 debuggerTemplate.size(),
252 (void*) const_cast<char*>(debuggerTemplate.c_str()),
253 MHD_RESPMEM_MUST_COPY);
jcgregorio6a2046e2016-01-28 05:31:31 -0800254 MHD_add_response_header (response, "Access-Control-Allow-Origin", "*");
joshualittccfdaa52016-01-27 07:40:29 -0800255
jcgregorio6f17bc52016-01-27 11:44:38 -0800256 int status = MHD_HTTP_OK;
257
joshualitt609d9792016-01-27 11:07:23 -0800258 if (redirect) {
259 MHD_add_response_header (response, "Location", redirectUrl);
jcgregorio6f17bc52016-01-27 11:44:38 -0800260 status = MHD_HTTP_SEE_OTHER;
joshualitt609d9792016-01-27 11:07:23 -0800261 }
262
jcgregorio6f17bc52016-01-27 11:44:38 -0800263 int ret = MHD_queue_response(connection, status, response);
joshualittccfdaa52016-01-27 07:40:29 -0800264 MHD_destroy_response(response);
265 return ret;
266}
267
joshualitt483b9012016-02-02 07:16:24 -0800268class UrlHandler {
269public:
270 virtual ~UrlHandler() {}
271 virtual bool canHandle(const char* method, const char* url) = 0;
272 virtual int handle(Request* request, MHD_Connection* connection,
joshualitt136f5172016-02-02 11:07:39 -0800273 const char* url, const char* method,
joshualitt483b9012016-02-02 07:16:24 -0800274 const char* upload_data, size_t* upload_data_size) = 0;
275};
joshualittccfdaa52016-01-27 07:40:29 -0800276
joshualitt29e5a892016-02-04 06:08:33 -0800277class CmdHandler : public UrlHandler {
joshualitt483b9012016-02-02 07:16:24 -0800278public:
279 bool canHandle(const char* method, const char* url) override {
joshualitt136f5172016-02-02 11:07:39 -0800280 const char* kBasePath = "/cmd";
281 return 0 == strncmp(url, kBasePath, strlen(kBasePath));
joshualittccfdaa52016-01-27 07:40:29 -0800282 }
283
joshualitt483b9012016-02-02 07:16:24 -0800284 int handle(Request* request, MHD_Connection* connection,
joshualitt136f5172016-02-02 11:07:39 -0800285 const char* url, const char* method,
joshualitt483b9012016-02-02 07:16:24 -0800286 const char* upload_data, size_t* upload_data_size) override {
joshualitt136f5172016-02-02 11:07:39 -0800287 SkTArray<SkString> commands;
288 SkStrSplit(url, "/", &commands);
289
290 if (!request->fPicture.get() || commands.count() > 3) {
291 return MHD_NO;
joshualitt483b9012016-02-02 07:16:24 -0800292 }
joshualitt136f5172016-02-02 11:07:39 -0800293
joshualittdb6a2542016-02-11 07:09:51 -0800294 // /cmd or /cmd/N
295 if (0 == strcmp(method, MHD_HTTP_METHOD_GET)) {
296 int n;
297 if (commands.count() == 1) {
ethannicholas0a0520a2016-02-12 12:06:53 -0800298 n = request->fDebugCanvas->getSize() - 1;
joshualittdb6a2542016-02-11 07:09:51 -0800299 } else {
300 sscanf(commands[1].c_str(), "%d", &n);
301 }
ethannicholas3ff5d8c2016-02-22 08:59:57 -0800302 return SendJSON(connection, request, n);
joshualitt136f5172016-02-02 11:07:39 -0800303 }
304
305 // /cmd/N, for now only delete supported
306 if (commands.count() == 2 && 0 == strcmp(method, MHD_HTTP_METHOD_DELETE)) {
307 int n;
308 sscanf(commands[1].c_str(), "%d", &n);
309 request->fDebugCanvas->deleteDrawCommandAt(n);
jcgregorio12d47ce2016-02-10 14:10:37 -0800310 return SendOK(connection);
joshualitt136f5172016-02-02 11:07:39 -0800311 }
312
313 // /cmd/N/[0|1]
314 if (commands.count() == 3 && 0 == strcmp(method, MHD_HTTP_METHOD_POST)) {
315 int n, toggle;
316 sscanf(commands[1].c_str(), "%d", &n);
317 sscanf(commands[2].c_str(), "%d", &toggle);
318 request->fDebugCanvas->toggleCommand(n, toggle);
jcgregorio12d47ce2016-02-10 14:10:37 -0800319 return SendOK(connection);
joshualitt136f5172016-02-02 11:07:39 -0800320 }
321
joshualitt483b9012016-02-02 07:16:24 -0800322 return MHD_NO;
323 }
324};
325
326class ImgHandler : public UrlHandler {
327public:
328 bool canHandle(const char* method, const char* url) override {
329 static const char* kBasePath = "/img";
330 return 0 == strcmp(method, MHD_HTTP_METHOD_GET) &&
331 0 == strncmp(url, kBasePath, strlen(kBasePath));
joshualittccfdaa52016-01-27 07:40:29 -0800332 }
333
joshualitt483b9012016-02-02 07:16:24 -0800334 int handle(Request* request, MHD_Connection* connection,
joshualitt136f5172016-02-02 11:07:39 -0800335 const char* url, const char* method,
joshualitt483b9012016-02-02 07:16:24 -0800336 const char* upload_data, size_t* upload_data_size) override {
joshualitt136f5172016-02-02 11:07:39 -0800337 SkTArray<SkString> commands;
338 SkStrSplit(url, "/", &commands);
339
340 if (!request->fPicture.get() || commands.count() > 2) {
341 return MHD_NO;
joshualitt483b9012016-02-02 07:16:24 -0800342 }
joshualitta341b902016-02-02 07:37:21 -0800343
joshualitt136f5172016-02-02 11:07:39 -0800344 int n;
345 // /img or /img/N
346 if (commands.count() == 1) {
347 n = request->fDebugCanvas->getSize() - 1;
348 } else {
349 sscanf(commands[1].c_str(), "%d", &n);
350 }
351
ethannicholas3ca1e1a2016-02-18 10:22:34 -0800352 SkAutoTUnref<SkData> data(drawToPng(request, n));
joshualitt136f5172016-02-02 11:07:39 -0800353 return SendData(connection, data, "image/png");
joshualitt483b9012016-02-02 07:16:24 -0800354 }
355};
joshualittccfdaa52016-01-27 07:40:29 -0800356
ethannicholas3ca1e1a2016-02-18 10:22:34 -0800357class BreakHandler : public UrlHandler {
358public:
359 bool canHandle(const char* method, const char* url) override {
360 static const char* kBasePath = "/break";
361 return 0 == strcmp(method, MHD_HTTP_METHOD_GET) &&
362 0 == strncmp(url, kBasePath, strlen(kBasePath));
363 }
364
365 static SkColor GetPixel(Request* request, int x, int y) {
366 SkCanvas* canvas = getCanvasFromRequest(request);
367 canvas->flush();
368 SkAutoTDelete<SkBitmap> bitmap(getBitmapFromCanvas(canvas));
369 SkASSERT(bitmap);
370 bitmap->lockPixels();
371 uint8_t* start = ((uint8_t*) bitmap->getPixels()) + (y * kImageWidth + x) * 4;
372 SkColor result = SkColorSetARGB(start[3], start[0], start[1], start[2]);
373 bitmap->unlockPixels();
374 return result;
375 }
376
377 int handle(Request* request, MHD_Connection* connection,
378 const char* url, const char* method,
379 const char* upload_data, size_t* upload_data_size) override {
380 SkTArray<SkString> commands;
381 SkStrSplit(url, "/", &commands);
382
383 if (!request->fPicture.get() || commands.count() != 4) {
384 return MHD_NO;
385 }
386
387 // /break/<n>/<x>/<y>
388 int n;
389 sscanf(commands[1].c_str(), "%d", &n);
390 int x;
391 sscanf(commands[2].c_str(), "%d", &x);
392 int y;
393 sscanf(commands[3].c_str(), "%d", &y);
394
395 int count = request->fDebugCanvas->getSize();
396 SkASSERT(n < count);
397
398 SkCanvas* canvas = getCanvasFromRequest(request);
399 canvas->clear(SK_ColorWHITE);
400 int saveCount = canvas->save();
401 for (int i = 0; i <= n; ++i) {
402 request->fDebugCanvas->getDrawCommandAt(i)->execute(canvas);
403 }
404 SkColor target = GetPixel(request, x, y);
405 Json::Value response(Json::objectValue);
406 Json::Value startColor(Json::arrayValue);
407 startColor.append(Json::Value(SkColorGetR(target)));
408 startColor.append(Json::Value(SkColorGetG(target)));
409 startColor.append(Json::Value(SkColorGetB(target)));
410 startColor.append(Json::Value(SkColorGetA(target)));
411 response["startColor"] = startColor;
412 response["endColor"] = startColor;
413 response["endOp"] = Json::Value(n);
414 for (int i = n + 1; i < n + count; ++i) {
415 int index = i % count;
416 if (index == 0) {
417 // reset canvas for wraparound
418 canvas->restoreToCount(saveCount);
419 canvas->clear(SK_ColorWHITE);
420 saveCount = canvas->save();
421 }
422 request->fDebugCanvas->getDrawCommandAt(index)->execute(canvas);
423 SkColor current = GetPixel(request, x, y);
424 if (current != target) {
425 Json::Value endColor(Json::arrayValue);
426 endColor.append(Json::Value(SkColorGetR(current)));
427 endColor.append(Json::Value(SkColorGetG(current)));
428 endColor.append(Json::Value(SkColorGetB(current)));
429 endColor.append(Json::Value(SkColorGetA(current)));
430 response["endColor"] = endColor;
431 response["endOp"] = Json::Value(index);
432 break;
433 }
434 }
435 canvas->restoreToCount(saveCount);
436 SkDynamicMemoryWStream stream;
437 stream.writeText(Json::FastWriter().write(response).c_str());
438 SkAutoTUnref<SkData> data(stream.copyToData());
439 return SendData(connection, data, "application/json");
440 }
441};
442
ethannicholas0a0520a2016-02-12 12:06:53 -0800443/**
444 Updates the clip visualization alpha. On all subsequent /img requests, the clip will be drawn in
445 black with the specified alpha. 0 = no visible clip, 255 = fully opaque clip.
446 */
447class ClipAlphaHandler : public UrlHandler {
448public:
449 bool canHandle(const char* method, const char* url) override {
450 static const char* kBasePath = "/clipAlpha/";
jcgregorio3341d422016-02-16 10:31:07 -0800451 return 0 == strcmp(method, MHD_HTTP_METHOD_POST) &&
ethannicholas0a0520a2016-02-12 12:06:53 -0800452 0 == strncmp(url, kBasePath, strlen(kBasePath));
453 }
454
455 int handle(Request* request, MHD_Connection* connection,
456 const char* url, const char* method,
457 const char* upload_data, size_t* upload_data_size) override {
458 SkTArray<SkString> commands;
459 SkStrSplit(url, "/", &commands);
460
461 if (!request->fPicture.get() || commands.count() != 2) {
462 return MHD_NO;
463 }
464
465 int alpha;
466 sscanf(commands[1].c_str(), "%d", &alpha);
467
468 request->fDebugCanvas->setClipVizColor(SkColorSetARGB(alpha, 0, 0, 0));
469 return SendOK(connection);
470 }
471};
472
ethannicholas85fca852016-02-19 08:40:59 -0800473/**
474 Controls whether GPU rendering is enabled. Posting to /enableGPU/1 turns GPU on, /enableGPU/0
475 disables it.
476 */
477class EnableGPUHandler : public UrlHandler {
478public:
479 bool canHandle(const char* method, const char* url) override {
480 static const char* kBasePath = "/enableGPU/";
481 return 0 == strcmp(method, MHD_HTTP_METHOD_POST) &&
482 0 == strncmp(url, kBasePath, strlen(kBasePath));
483 }
484
485 int handle(Request* request, MHD_Connection* connection,
486 const char* url, const char* method,
487 const char* upload_data, size_t* upload_data_size) override {
488 SkTArray<SkString> commands;
489 SkStrSplit(url, "/", &commands);
490
491 if (commands.count() != 2) {
492 return MHD_NO;
493 }
494
495 int enable;
496 sscanf(commands[1].c_str(), "%d", &enable);
497
498 if (enable) {
499 SkSurface* surface = createGPUSurface(request);
500 if (surface) {
501 request->fSurface.reset(surface);
ethannicholas3ff5d8c2016-02-22 08:59:57 -0800502 request->fGPUEnabled = true;
ethannicholas85fca852016-02-19 08:40:59 -0800503 return SendOK(connection);
504 }
505 return SendError(connection, "Unable to create GPU surface");
506 }
507 request->fSurface.reset(createCPUSurface());
ethannicholas3ff5d8c2016-02-22 08:59:57 -0800508 request->fGPUEnabled = false;
ethannicholas85fca852016-02-19 08:40:59 -0800509 return SendOK(connection);
510 }
511};
512
joshualitt483b9012016-02-02 07:16:24 -0800513class PostHandler : public UrlHandler {
514public:
515 bool canHandle(const char* method, const char* url) override {
516 return 0 == strcmp(method, MHD_HTTP_METHOD_POST) &&
517 0 == strcmp(url, "/new");
joshualittccfdaa52016-01-27 07:40:29 -0800518 }
519
joshualitt483b9012016-02-02 07:16:24 -0800520 int handle(Request* request, MHD_Connection* connection,
joshualitt136f5172016-02-02 11:07:39 -0800521 const char* url, const char* method,
joshualitt483b9012016-02-02 07:16:24 -0800522 const char* upload_data, size_t* upload_data_size) override {
523 UploadContext* uc = request->fUploadContext;
joshualittccfdaa52016-01-27 07:40:29 -0800524
joshualitt483b9012016-02-02 07:16:24 -0800525 // New connection
526 if (!uc) {
527 // TODO make this a method on request
528 uc = new UploadContext;
529 uc->connection = connection;
530 uc->fPostProcessor = MHD_create_post_processor(connection, kBufferSize,
531 &process_upload_data, uc);
532 SkASSERT(uc->fPostProcessor);
joshualitt609d9792016-01-27 11:07:23 -0800533
joshualitt483b9012016-02-02 07:16:24 -0800534 request->fUploadContext = uc;
535 return MHD_YES;
536 }
537
538 // in process upload
539 if (0 != *upload_data_size) {
540 SkASSERT(uc->fPostProcessor);
541 MHD_post_process(uc->fPostProcessor, upload_data, *upload_data_size);
542 *upload_data_size = 0;
543 return MHD_YES;
544 }
545
546 // end of upload
547 MHD_destroy_post_processor(uc->fPostProcessor);
548 uc->fPostProcessor = nullptr;
549
joshualitt136f5172016-02-02 11:07:39 -0800550 // parse picture from stream
551 request->fPicture.reset(
552 SkPicture::CreateFromStream(request->fUploadContext->fStream.detachAsStream()));
553 if (!request->fPicture.get()) {
554 fprintf(stderr, "Could not create picture from stream.\n");
555 return MHD_NO;
joshualitt483b9012016-02-02 07:16:24 -0800556 }
557
joshualitt136f5172016-02-02 11:07:39 -0800558 // pour picture into debug canvas
559 request->fDebugCanvas.reset(new SkDebugCanvas(kImageWidth, kImageHeight));
560 request->fDebugCanvas->drawPicture(request->fPicture);
561
joshualitta341b902016-02-02 07:37:21 -0800562 // clear upload context
563 delete request->fUploadContext;
564 request->fUploadContext = nullptr;
565
joshualitt483b9012016-02-02 07:16:24 -0800566 return SendTemplate(connection, true, "/");
joshualittccfdaa52016-01-27 07:40:29 -0800567 }
joshualitt483b9012016-02-02 07:16:24 -0800568};
569
joshualitt792345f2016-02-02 13:02:33 -0800570class DownloadHandler : public UrlHandler {
571public:
572 bool canHandle(const char* method, const char* url) override {
573 return 0 == strcmp(method, MHD_HTTP_METHOD_GET) &&
574 0 == strcmp(url, "/download");
575 }
576
577 int handle(Request* request, MHD_Connection* connection,
578 const char* url, const char* method,
579 const char* upload_data, size_t* upload_data_size) override {
580 if (!request->fPicture.get()) {
581 return MHD_NO;
582 }
583
584 // TODO move to a function
585 // Playback into picture recorder
586 SkPictureRecorder recorder;
587 SkCanvas* canvas = recorder.beginRecording(kImageWidth, kImageHeight);
588
589 request->fDebugCanvas->draw(canvas);
590
591 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
592
593 SkDynamicMemoryWStream outStream;
594
595 SkAutoTUnref<SkPixelSerializer> serializer(SkImageEncoder::CreatePixelSerializer());
596 picture->serialize(&outStream, serializer);
597
598 SkAutoTUnref<SkData> data(outStream.copyToData());
599
600 // TODO fancier name handling
601 return SendData(connection, data, "application/octet-stream", true,
602 "attachment; filename=something.skp;");
603 }
604};
605
joshualitt29e5a892016-02-04 06:08:33 -0800606class InfoHandler : public UrlHandler {
607public:
608 bool canHandle(const char* method, const char* url) override {
609 const char* kBaseName = "/info";
610 return 0 == strcmp(method, MHD_HTTP_METHOD_GET) &&
611 0 == strncmp(url, kBaseName, strlen(kBaseName));
612 }
613
614 int handle(Request* request, MHD_Connection* connection,
615 const char* url, const char* method,
616 const char* upload_data, size_t* upload_data_size) override {
617 SkTArray<SkString> commands;
618 SkStrSplit(url, "/", &commands);
619
620 if (!request->fPicture.get() || commands.count() > 2) {
621 return MHD_NO;
622 }
623
624 // drawTo
ethannicholas85fca852016-02-19 08:40:59 -0800625 SkAutoTUnref<SkSurface> surface(createCPUSurface());
joshualitt29e5a892016-02-04 06:08:33 -0800626 SkCanvas* canvas = surface->getCanvas();
627
628 int n;
629 // /info or /info/N
630 if (commands.count() == 1) {
631 n = request->fDebugCanvas->getSize() - 1;
632 } else {
633 sscanf(commands[1].c_str(), "%d", &n);
634 }
635
636 // TODO this is really slow and we should cache the matrix and clip
637 request->fDebugCanvas->drawTo(canvas, n);
638
639 // make some json
640 SkMatrix vm = request->fDebugCanvas->getCurrentMatrix();
641 SkIRect clip = request->fDebugCanvas->getCurrentClip();
642 Json::Value info(Json::objectValue);
643 info["ViewMatrix"] = SkJSONCanvas::MakeMatrix(vm);
644 info["ClipRect"] = SkJSONCanvas::MakeIRect(clip);
645
646 std::string json = Json::FastWriter().write(info);
647
648 // We don't want the null terminator so strlen is correct
649 SkAutoTUnref<SkData> data(SkData::NewWithCopy(json.c_str(), strlen(json.c_str())));
650 return SendData(connection, data, "application/json");
651 }
652};
653
joshualittcdad12f2016-02-08 07:08:21 -0800654class DataHandler : public UrlHandler {
655public:
656 bool canHandle(const char* method, const char* url) override {
657 static const char* kBaseUrl = "/data";
658 return 0 == strcmp(method, MHD_HTTP_METHOD_GET) &&
659 0 == strncmp(url, kBaseUrl, strlen(kBaseUrl));
660 }
661
662 int handle(Request* request, MHD_Connection* connection,
663 const char* url, const char* method,
664 const char* upload_data, size_t* upload_data_size) override {
665 SkTArray<SkString> commands;
666 SkStrSplit(url, "/", &commands);
667
668 if (!request->fPicture.get() || commands.count() != 2) {
669 return MHD_NO;
670 }
671
672 SkAutoTUnref<UrlDataManager::UrlData> urlData(
673 SkRef(request->fUrlDataManager.getDataFromUrl(SkString(url))));
674
675 if (urlData) {
676 return SendData(connection, urlData->fData.get(), urlData->fContentType.c_str());
677 }
678 return MHD_NO;
679 }
680};
joshualitt29e5a892016-02-04 06:08:33 -0800681
joshualitt483b9012016-02-02 07:16:24 -0800682class RootHandler : public UrlHandler {
683public:
684 bool canHandle(const char* method, const char* url) override {
685 return 0 == strcmp(method, MHD_HTTP_METHOD_GET) &&
686 0 == strcmp(url, "/");
687 }
688
689 int handle(Request* request, MHD_Connection* connection,
joshualitt136f5172016-02-02 11:07:39 -0800690 const char* url, const char* method,
joshualitt483b9012016-02-02 07:16:24 -0800691 const char* upload_data, size_t* upload_data_size) override {
692 return SendTemplate(connection);
693 }
694};
joshualittccfdaa52016-01-27 07:40:29 -0800695
696class UrlManager {
697public:
698 UrlManager() {
699 // Register handlers
joshualitt483b9012016-02-02 07:16:24 -0800700 fHandlers.push_back(new RootHandler);
701 fHandlers.push_back(new PostHandler);
702 fHandlers.push_back(new ImgHandler);
ethannicholas0a0520a2016-02-12 12:06:53 -0800703 fHandlers.push_back(new ClipAlphaHandler);
ethannicholas85fca852016-02-19 08:40:59 -0800704 fHandlers.push_back(new EnableGPUHandler);
joshualitt29e5a892016-02-04 06:08:33 -0800705 fHandlers.push_back(new CmdHandler);
joshualitt483b9012016-02-02 07:16:24 -0800706 fHandlers.push_back(new InfoHandler);
joshualitt792345f2016-02-02 13:02:33 -0800707 fHandlers.push_back(new DownloadHandler);
joshualittcdad12f2016-02-08 07:08:21 -0800708 fHandlers.push_back(new DataHandler);
ethannicholas3ca1e1a2016-02-18 10:22:34 -0800709 fHandlers.push_back(new BreakHandler);
joshualitt483b9012016-02-02 07:16:24 -0800710 }
711
712 ~UrlManager() {
713 for (int i = 0; i < fHandlers.count(); i++) { delete fHandlers[i]; }
joshualittccfdaa52016-01-27 07:40:29 -0800714 }
715
716 // This is clearly not efficient for a large number of urls and handlers
717 int invoke(Request* request, MHD_Connection* connection, const char* url, const char* method,
718 const char* upload_data, size_t* upload_data_size) const {
719 for (int i = 0; i < fHandlers.count(); i++) {
joshualitt483b9012016-02-02 07:16:24 -0800720 if (fHandlers[i]->canHandle(method, url)) {
joshualitt136f5172016-02-02 11:07:39 -0800721 return fHandlers[i]->handle(request, connection, url, method, upload_data,
722 upload_data_size);
joshualittccfdaa52016-01-27 07:40:29 -0800723 }
724 }
725 return MHD_NO;
726 }
727
728private:
joshualitt483b9012016-02-02 07:16:24 -0800729 SkTArray<UrlHandler*> fHandlers;
joshualittccfdaa52016-01-27 07:40:29 -0800730};
731
732const UrlManager kUrlManager;
733
joshualitt7f6a1e02016-01-22 11:21:43 -0800734int answer_to_connection(void* cls, struct MHD_Connection* connection,
735 const char* url, const char* method, const char* version,
736 const char* upload_data, size_t* upload_data_size,
737 void** con_cls) {
joshualitt9a4e1882016-01-27 07:03:29 -0800738 SkDebugf("New %s request for %s using version %s\n", method, url, version);
joshualitt7f6a1e02016-01-22 11:21:43 -0800739
joshualitt9a4e1882016-01-27 07:03:29 -0800740 Request* request = reinterpret_cast<Request*>(cls);
joshualitt483b9012016-02-02 07:16:24 -0800741 int result = kUrlManager.invoke(request, connection, url, method, upload_data,
742 upload_data_size);
743 if (MHD_NO == result) {
joshualitt873d6242016-02-08 13:57:44 -0800744 fprintf(stderr, "Invalid method and / or url: %s %s\n", method, url);
joshualitt483b9012016-02-02 07:16:24 -0800745 }
746 return result;
joshualitt7f6a1e02016-01-22 11:21:43 -0800747}
748
749int skiaserve_main() {
joshualittcdad12f2016-02-08 07:08:21 -0800750 Request request(SkString("/data")); // This simple server has one request
ethannicholas85fca852016-02-19 08:40:59 -0800751
752 // create surface
753 GrContextOptions grContextOpts;
754 request.fContextFactory.reset(new GrContextFactory(grContextOpts));
755 request.fSurface.reset(createCPUSurface());
756
joshualitt7f6a1e02016-01-22 11:21:43 -0800757 struct MHD_Daemon* daemon;
jcgregorio21ab1202016-01-28 06:24:19 -0800758 // TODO Add option to bind this strictly to an address, e.g. localhost, for security.
759 daemon = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, FLAGS_port, nullptr, nullptr,
joshualitt9a4e1882016-01-27 07:03:29 -0800760 &answer_to_connection, &request,
761 MHD_OPTION_END);
joshualitt7f6a1e02016-01-22 11:21:43 -0800762 if (NULL == daemon) {
763 return 1;
764 }
765
766 getchar();
767 MHD_stop_daemon(daemon);
768 return 0;
769}
770
771#if !defined SK_BUILD_FOR_IOS
772int main(int argc, char** argv) {
773 SkCommandLineFlags::Parse(argc, argv);
774 return skiaserve_main();
775}
776#endif