OLD | NEW |
1 /* | 1 /* |
2 * Copyright 2016 Google Inc. | 2 * Copyright 2016 Google Inc. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
6 */ | 6 */ |
7 | 7 |
8 #include "GrCaps.h" | 8 #include "GrCaps.h" |
9 #include "GrContextFactory.h" | 9 #include "GrContextFactory.h" |
10 | 10 |
11 #include "Request.h" | 11 #include "Request.h" |
12 #include "Response.h" | 12 #include "Response.h" |
13 | 13 |
14 #include "SkCanvas.h" | |
15 #include "SkCommandLineFlags.h" | 14 #include "SkCommandLineFlags.h" |
16 #include "SkJSONCanvas.h" | |
17 #include "SkPictureRecorder.h" | |
18 #include "SkPixelSerializer.h" | |
19 | 15 |
| 16 #include "microhttpd.h" |
| 17 |
| 18 #include "urlhandlers/UrlHandler.h" |
20 #include <sys/socket.h> | 19 #include <sys/socket.h> |
21 #include <microhttpd.h> | |
22 | 20 |
23 using namespace Response; | 21 using namespace Response; |
24 | 22 |
25 // To get image decoders linked in we have to do the below magic | 23 // To get image decoders linked in we have to do the below magic |
26 #include "SkForceLinking.h" | 24 #include "SkForceLinking.h" |
27 #include "SkImageDecoder.h" | 25 #include "SkImageDecoder.h" |
28 __SK_FORCE_IMAGE_DECODER_LINKING; | 26 __SK_FORCE_IMAGE_DECODER_LINKING; |
29 | 27 |
30 DEFINE_int32(port, 8888, "The port to listen on."); | 28 DEFINE_int32(port, 8888, "The port to listen on."); |
31 | 29 |
32 static const size_t kBufferSize = 1024; | |
33 | |
34 static int process_upload_data(void* cls, enum MHD_ValueKind kind, | |
35 const char* key, const char* filename, | |
36 const char* content_type, const char* transfer_en
coding, | |
37 const char* data, uint64_t off, size_t size) { | |
38 struct UploadContext* uc = reinterpret_cast<UploadContext*>(cls); | |
39 | |
40 if (0 != size) { | |
41 uc->fStream.write(data, size); | |
42 } | |
43 return MHD_YES; | |
44 } | |
45 | |
46 class UrlHandler { | |
47 public: | |
48 virtual ~UrlHandler() {} | |
49 virtual bool canHandle(const char* method, const char* url) = 0; | |
50 virtual int handle(Request* request, MHD_Connection* connection, | |
51 const char* url, const char* method, | |
52 const char* upload_data, size_t* upload_data_size) = 0; | |
53 }; | |
54 | |
55 class CmdHandler : public UrlHandler { | |
56 public: | |
57 bool canHandle(const char* method, const char* url) override { | |
58 const char* kBasePath = "/cmd"; | |
59 return 0 == strncmp(url, kBasePath, strlen(kBasePath)); | |
60 } | |
61 | |
62 int handle(Request* request, MHD_Connection* connection, | |
63 const char* url, const char* method, | |
64 const char* upload_data, size_t* upload_data_size) override { | |
65 SkTArray<SkString> commands; | |
66 SkStrSplit(url, "/", &commands); | |
67 | |
68 if (!request->fPicture.get() || commands.count() > 3) { | |
69 return MHD_NO; | |
70 } | |
71 | |
72 // /cmd or /cmd/N | |
73 if (0 == strcmp(method, MHD_HTTP_METHOD_GET)) { | |
74 int n; | |
75 if (commands.count() == 1) { | |
76 n = request->fDebugCanvas->getSize() - 1; | |
77 } else { | |
78 sscanf(commands[1].c_str(), "%d", &n); | |
79 } | |
80 return SendJSON(connection, request, n); | |
81 } | |
82 | |
83 // /cmd/N, for now only delete supported | |
84 if (commands.count() == 2 && 0 == strcmp(method, MHD_HTTP_METHOD_DELETE)
) { | |
85 int n; | |
86 sscanf(commands[1].c_str(), "%d", &n); | |
87 request->fDebugCanvas->deleteDrawCommandAt(n); | |
88 return SendOK(connection); | |
89 } | |
90 | |
91 // /cmd/N/[0|1] | |
92 if (commands.count() == 3 && 0 == strcmp(method, MHD_HTTP_METHOD_POST))
{ | |
93 int n, toggle; | |
94 sscanf(commands[1].c_str(), "%d", &n); | |
95 sscanf(commands[2].c_str(), "%d", &toggle); | |
96 request->fDebugCanvas->toggleCommand(n, toggle); | |
97 return SendOK(connection); | |
98 } | |
99 | |
100 return MHD_NO; | |
101 } | |
102 }; | |
103 | |
104 class ImgHandler : public UrlHandler { | |
105 public: | |
106 bool canHandle(const char* method, const char* url) override { | |
107 static const char* kBasePath = "/img"; | |
108 return 0 == strcmp(method, MHD_HTTP_METHOD_GET) && | |
109 0 == strncmp(url, kBasePath, strlen(kBasePath)); | |
110 } | |
111 | |
112 int handle(Request* request, MHD_Connection* connection, | |
113 const char* url, const char* method, | |
114 const char* upload_data, size_t* upload_data_size) override { | |
115 SkTArray<SkString> commands; | |
116 SkStrSplit(url, "/", &commands); | |
117 | |
118 if (!request->fPicture.get() || commands.count() > 2) { | |
119 return MHD_NO; | |
120 } | |
121 | |
122 int n; | |
123 // /img or /img/N | |
124 if (commands.count() == 1) { | |
125 n = request->fDebugCanvas->getSize() - 1; | |
126 } else { | |
127 sscanf(commands[1].c_str(), "%d", &n); | |
128 } | |
129 | |
130 SkAutoTUnref<SkData> data(request->drawToPng(n)); | |
131 return SendData(connection, data, "image/png"); | |
132 } | |
133 }; | |
134 | |
135 class BreakHandler : public UrlHandler { | |
136 public: | |
137 bool canHandle(const char* method, const char* url) override { | |
138 static const char* kBasePath = "/break"; | |
139 return 0 == strcmp(method, MHD_HTTP_METHOD_GET) && | |
140 0 == strncmp(url, kBasePath, strlen(kBasePath)); | |
141 } | |
142 | |
143 static SkColor GetPixel(Request* request, int x, int y) { | |
144 SkCanvas* canvas = request->getCanvas(); | |
145 canvas->flush(); | |
146 SkAutoTDelete<SkBitmap> bitmap(request->getBitmapFromCanvas(canvas)); | |
147 SkASSERT(bitmap); | |
148 bitmap->lockPixels(); | |
149 uint8_t* start = ((uint8_t*) bitmap->getPixels()) + (y * Request::kImage
Width + x) * 4; | |
150 SkColor result = SkColorSetARGB(start[3], start[0], start[1], start[2]); | |
151 bitmap->unlockPixels(); | |
152 return result; | |
153 } | |
154 | |
155 int handle(Request* request, MHD_Connection* connection, | |
156 const char* url, const char* method, | |
157 const char* upload_data, size_t* upload_data_size) override { | |
158 SkTArray<SkString> commands; | |
159 SkStrSplit(url, "/", &commands); | |
160 | |
161 if (!request->fPicture.get() || commands.count() != 4) { | |
162 return MHD_NO; | |
163 } | |
164 | |
165 // /break/<n>/<x>/<y> | |
166 int n; | |
167 sscanf(commands[1].c_str(), "%d", &n); | |
168 int x; | |
169 sscanf(commands[2].c_str(), "%d", &x); | |
170 int y; | |
171 sscanf(commands[3].c_str(), "%d", &y); | |
172 | |
173 int count = request->fDebugCanvas->getSize(); | |
174 SkASSERT(n < count); | |
175 | |
176 SkCanvas* canvas = request->getCanvas(); | |
177 canvas->clear(SK_ColorWHITE); | |
178 int saveCount = canvas->save(); | |
179 for (int i = 0; i <= n; ++i) { | |
180 request->fDebugCanvas->getDrawCommandAt(i)->execute(canvas); | |
181 } | |
182 SkColor target = GetPixel(request, x, y); | |
183 Json::Value response(Json::objectValue); | |
184 Json::Value startColor(Json::arrayValue); | |
185 startColor.append(Json::Value(SkColorGetR(target))); | |
186 startColor.append(Json::Value(SkColorGetG(target))); | |
187 startColor.append(Json::Value(SkColorGetB(target))); | |
188 startColor.append(Json::Value(SkColorGetA(target))); | |
189 response["startColor"] = startColor; | |
190 response["endColor"] = startColor; | |
191 response["endOp"] = Json::Value(n); | |
192 for (int i = n + 1; i < n + count; ++i) { | |
193 int index = i % count; | |
194 if (index == 0) { | |
195 // reset canvas for wraparound | |
196 canvas->restoreToCount(saveCount); | |
197 canvas->clear(SK_ColorWHITE); | |
198 saveCount = canvas->save(); | |
199 } | |
200 request->fDebugCanvas->getDrawCommandAt(index)->execute(canvas); | |
201 SkColor current = GetPixel(request, x, y); | |
202 if (current != target) { | |
203 Json::Value endColor(Json::arrayValue); | |
204 endColor.append(Json::Value(SkColorGetR(current))); | |
205 endColor.append(Json::Value(SkColorGetG(current))); | |
206 endColor.append(Json::Value(SkColorGetB(current))); | |
207 endColor.append(Json::Value(SkColorGetA(current))); | |
208 response["endColor"] = endColor; | |
209 response["endOp"] = Json::Value(index); | |
210 break; | |
211 } | |
212 } | |
213 canvas->restoreToCount(saveCount); | |
214 SkDynamicMemoryWStream stream; | |
215 stream.writeText(Json::FastWriter().write(response).c_str()); | |
216 SkAutoTUnref<SkData> data(stream.copyToData()); | |
217 return SendData(connection, data, "application/json"); | |
218 } | |
219 }; | |
220 | |
221 /** | |
222 Updates the clip visualization alpha. On all subsequent /img requests, the cl
ip will be drawn in | |
223 black with the specified alpha. 0 = no visible clip, 255 = fully opaque clip. | |
224 */ | |
225 class ClipAlphaHandler : public UrlHandler { | |
226 public: | |
227 bool canHandle(const char* method, const char* url) override { | |
228 static const char* kBasePath = "/clipAlpha/"; | |
229 return 0 == strcmp(method, MHD_HTTP_METHOD_POST) && | |
230 0 == strncmp(url, kBasePath, strlen(kBasePath)); | |
231 } | |
232 | |
233 int handle(Request* request, MHD_Connection* connection, | |
234 const char* url, const char* method, | |
235 const char* upload_data, size_t* upload_data_size) override { | |
236 SkTArray<SkString> commands; | |
237 SkStrSplit(url, "/", &commands); | |
238 | |
239 if (!request->fPicture.get() || commands.count() != 2) { | |
240 return MHD_NO; | |
241 } | |
242 | |
243 int alpha; | |
244 sscanf(commands[1].c_str(), "%d", &alpha); | |
245 | |
246 request->fDebugCanvas->setClipVizColor(SkColorSetARGB(alpha, 0, 0, 0)); | |
247 return SendOK(connection); | |
248 } | |
249 }; | |
250 | |
251 /** | |
252 Controls whether GPU rendering is enabled. Posting to /enableGPU/1 turns GPU
on, /enableGPU/0 | |
253 disables it. | |
254 */ | |
255 class EnableGPUHandler : public UrlHandler { | |
256 public: | |
257 bool canHandle(const char* method, const char* url) override { | |
258 static const char* kBasePath = "/enableGPU/"; | |
259 return 0 == strcmp(method, MHD_HTTP_METHOD_POST) && | |
260 0 == strncmp(url, kBasePath, strlen(kBasePath)); | |
261 } | |
262 | |
263 int handle(Request* request, MHD_Connection* connection, | |
264 const char* url, const char* method, | |
265 const char* upload_data, size_t* upload_data_size) override { | |
266 SkTArray<SkString> commands; | |
267 SkStrSplit(url, "/", &commands); | |
268 | |
269 if (commands.count() != 2) { | |
270 return MHD_NO; | |
271 } | |
272 | |
273 int enable; | |
274 sscanf(commands[1].c_str(), "%d", &enable); | |
275 | |
276 if (enable) { | |
277 SkSurface* surface = request->createGPUSurface(); | |
278 if (surface) { | |
279 request->fSurface.reset(surface); | |
280 request->fGPUEnabled = true; | |
281 return SendOK(connection); | |
282 } | |
283 return SendError(connection, "Unable to create GPU surface"); | |
284 } | |
285 request->fSurface.reset(request->createCPUSurface()); | |
286 request->fGPUEnabled = false; | |
287 return SendOK(connection); | |
288 } | |
289 }; | |
290 | |
291 class PostHandler : public UrlHandler { | |
292 public: | |
293 bool canHandle(const char* method, const char* url) override { | |
294 return 0 == strcmp(method, MHD_HTTP_METHOD_POST) && | |
295 0 == strcmp(url, "/new"); | |
296 } | |
297 | |
298 int handle(Request* request, MHD_Connection* connection, | |
299 const char* url, const char* method, | |
300 const char* upload_data, size_t* upload_data_size) override { | |
301 UploadContext* uc = request->fUploadContext; | |
302 | |
303 // New connection | |
304 if (!uc) { | |
305 // TODO make this a method on request | |
306 uc = new UploadContext; | |
307 uc->connection = connection; | |
308 uc->fPostProcessor = MHD_create_post_processor(connection, kBufferSi
ze, | |
309 &process_upload_data,
uc); | |
310 SkASSERT(uc->fPostProcessor); | |
311 | |
312 request->fUploadContext = uc; | |
313 return MHD_YES; | |
314 } | |
315 | |
316 // in process upload | |
317 if (0 != *upload_data_size) { | |
318 SkASSERT(uc->fPostProcessor); | |
319 MHD_post_process(uc->fPostProcessor, upload_data, *upload_data_size)
; | |
320 *upload_data_size = 0; | |
321 return MHD_YES; | |
322 } | |
323 | |
324 // end of upload | |
325 MHD_destroy_post_processor(uc->fPostProcessor); | |
326 uc->fPostProcessor = nullptr; | |
327 | |
328 // parse picture from stream | |
329 request->fPicture.reset( | |
330 SkPicture::CreateFromStream(request->fUploadContext->fStream.detachA
sStream())); | |
331 if (!request->fPicture.get()) { | |
332 fprintf(stderr, "Could not create picture from stream.\n"); | |
333 return MHD_NO; | |
334 } | |
335 | |
336 // pour picture into debug canvas | |
337 request->fDebugCanvas.reset(new SkDebugCanvas(Request::kImageWidth, | |
338 Request::kImageHeight)); | |
339 request->fDebugCanvas->drawPicture(request->fPicture); | |
340 | |
341 // clear upload context | |
342 delete request->fUploadContext; | |
343 request->fUploadContext = nullptr; | |
344 | |
345 return SendTemplate(connection, true, "/"); | |
346 } | |
347 }; | |
348 | |
349 class DownloadHandler : public UrlHandler { | |
350 public: | |
351 bool canHandle(const char* method, const char* url) override { | |
352 return 0 == strcmp(method, MHD_HTTP_METHOD_GET) && | |
353 0 == strcmp(url, "/download"); | |
354 } | |
355 | |
356 int handle(Request* request, MHD_Connection* connection, | |
357 const char* url, const char* method, | |
358 const char* upload_data, size_t* upload_data_size) override { | |
359 if (!request->fPicture.get()) { | |
360 return MHD_NO; | |
361 } | |
362 | |
363 // TODO move to a function | |
364 // Playback into picture recorder | |
365 SkPictureRecorder recorder; | |
366 SkCanvas* canvas = recorder.beginRecording(Request::kImageWidth, | |
367 Request::kImageHeight); | |
368 | |
369 request->fDebugCanvas->draw(canvas); | |
370 | |
371 SkAutoTUnref<SkPicture> picture(recorder.endRecording()); | |
372 | |
373 SkDynamicMemoryWStream outStream; | |
374 | |
375 SkAutoTUnref<SkPixelSerializer> serializer(SkImageEncoder::CreatePixelSe
rializer()); | |
376 picture->serialize(&outStream, serializer); | |
377 | |
378 SkAutoTUnref<SkData> data(outStream.copyToData()); | |
379 | |
380 // TODO fancier name handling | |
381 return SendData(connection, data, "application/octet-stream", true, | |
382 "attachment; filename=something.skp;"); | |
383 } | |
384 }; | |
385 | |
386 class InfoHandler : public UrlHandler { | |
387 public: | |
388 bool canHandle(const char* method, const char* url) override { | |
389 const char* kBaseName = "/info"; | |
390 return 0 == strcmp(method, MHD_HTTP_METHOD_GET) && | |
391 0 == strncmp(url, kBaseName, strlen(kBaseName)); | |
392 } | |
393 | |
394 int handle(Request* request, MHD_Connection* connection, | |
395 const char* url, const char* method, | |
396 const char* upload_data, size_t* upload_data_size) override { | |
397 SkTArray<SkString> commands; | |
398 SkStrSplit(url, "/", &commands); | |
399 | |
400 if (!request->fPicture.get() || commands.count() > 2) { | |
401 return MHD_NO; | |
402 } | |
403 | |
404 // drawTo | |
405 SkAutoTUnref<SkSurface> surface(request->createCPUSurface()); | |
406 SkCanvas* canvas = surface->getCanvas(); | |
407 | |
408 int n; | |
409 // /info or /info/N | |
410 if (commands.count() == 1) { | |
411 n = request->fDebugCanvas->getSize() - 1; | |
412 } else { | |
413 sscanf(commands[1].c_str(), "%d", &n); | |
414 } | |
415 | |
416 // TODO this is really slow and we should cache the matrix and clip | |
417 request->fDebugCanvas->drawTo(canvas, n); | |
418 | |
419 // make some json | |
420 SkMatrix vm = request->fDebugCanvas->getCurrentMatrix(); | |
421 SkIRect clip = request->fDebugCanvas->getCurrentClip(); | |
422 Json::Value info(Json::objectValue); | |
423 info["ViewMatrix"] = SkJSONCanvas::MakeMatrix(vm); | |
424 info["ClipRect"] = SkJSONCanvas::MakeIRect(clip); | |
425 | |
426 std::string json = Json::FastWriter().write(info); | |
427 | |
428 // We don't want the null terminator so strlen is correct | |
429 SkAutoTUnref<SkData> data(SkData::NewWithCopy(json.c_str(), strlen(json.
c_str()))); | |
430 return SendData(connection, data, "application/json"); | |
431 } | |
432 }; | |
433 | |
434 class DataHandler : public UrlHandler { | |
435 public: | |
436 bool canHandle(const char* method, const char* url) override { | |
437 static const char* kBaseUrl = "/data"; | |
438 return 0 == strcmp(method, MHD_HTTP_METHOD_GET) && | |
439 0 == strncmp(url, kBaseUrl, strlen(kBaseUrl)); | |
440 } | |
441 | |
442 int handle(Request* request, MHD_Connection* connection, | |
443 const char* url, const char* method, | |
444 const char* upload_data, size_t* upload_data_size) override { | |
445 SkTArray<SkString> commands; | |
446 SkStrSplit(url, "/", &commands); | |
447 | |
448 if (!request->fPicture.get() || commands.count() != 2) { | |
449 return MHD_NO; | |
450 } | |
451 | |
452 SkAutoTUnref<UrlDataManager::UrlData> urlData( | |
453 SkRef(request->fUrlDataManager.getDataFromUrl(SkString(url)))); | |
454 | |
455 if (urlData) { | |
456 return SendData(connection, urlData->fData.get(), urlData->fContentT
ype.c_str()); | |
457 } | |
458 return MHD_NO; | |
459 } | |
460 }; | |
461 | |
462 class RootHandler : public UrlHandler { | |
463 public: | |
464 bool canHandle(const char* method, const char* url) override { | |
465 return 0 == strcmp(method, MHD_HTTP_METHOD_GET) && | |
466 0 == strcmp(url, "/"); | |
467 } | |
468 | |
469 int handle(Request* request, MHD_Connection* connection, | |
470 const char* url, const char* method, | |
471 const char* upload_data, size_t* upload_data_size) override { | |
472 return SendTemplate(connection); | |
473 } | |
474 }; | |
475 | |
476 class UrlManager { | 30 class UrlManager { |
477 public: | 31 public: |
478 UrlManager() { | 32 UrlManager() { |
479 // Register handlers | 33 // Register handlers |
480 fHandlers.push_back(new RootHandler); | 34 fHandlers.push_back(new RootHandler); |
481 fHandlers.push_back(new PostHandler); | 35 fHandlers.push_back(new PostHandler); |
482 fHandlers.push_back(new ImgHandler); | 36 fHandlers.push_back(new ImgHandler); |
483 fHandlers.push_back(new ClipAlphaHandler); | 37 fHandlers.push_back(new ClipAlphaHandler); |
484 fHandlers.push_back(new EnableGPUHandler); | 38 fHandlers.push_back(new EnableGPUHandler); |
485 fHandlers.push_back(new CmdHandler); | 39 fHandlers.push_back(new CmdHandler); |
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
547 MHD_stop_daemon(daemon); | 101 MHD_stop_daemon(daemon); |
548 return 0; | 102 return 0; |
549 } | 103 } |
550 | 104 |
551 #if !defined SK_BUILD_FOR_IOS | 105 #if !defined SK_BUILD_FOR_IOS |
552 int main(int argc, char** argv) { | 106 int main(int argc, char** argv) { |
553 SkCommandLineFlags::Parse(argc, argv); | 107 SkCommandLineFlags::Parse(argc, argv); |
554 return skiaserve_main(); | 108 return skiaserve_main(); |
555 } | 109 } |
556 #endif | 110 #endif |
OLD | NEW |