| 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 |