Index: tools/skiaserve/skiaserve.cpp |
diff --git a/tools/skiaserve/skiaserve.cpp b/tools/skiaserve/skiaserve.cpp |
index d5a0132d0d189833ed8d48c927c1ba4f56330471..622fbf114ba8d567844c549c606e034de555d384 100644 |
--- a/tools/skiaserve/skiaserve.cpp |
+++ b/tools/skiaserve/skiaserve.cpp |
@@ -11,14 +11,12 @@ |
#include "Request.h" |
#include "Response.h" |
-#include "SkCanvas.h" |
#include "SkCommandLineFlags.h" |
-#include "SkJSONCanvas.h" |
-#include "SkPictureRecorder.h" |
-#include "SkPixelSerializer.h" |
+#include "microhttpd.h" |
+ |
+#include "urlhandlers/UrlHandler.h" |
#include <sys/socket.h> |
-#include <microhttpd.h> |
using namespace Response; |
@@ -29,450 +27,6 @@ __SK_FORCE_IMAGE_DECODER_LINKING; |
DEFINE_int32(port, 8888, "The port to listen on."); |
-static const size_t kBufferSize = 1024; |
- |
-static int process_upload_data(void* cls, enum MHD_ValueKind kind, |
- const char* key, const char* filename, |
- const char* content_type, const char* transfer_encoding, |
- const char* data, uint64_t off, size_t size) { |
- struct UploadContext* uc = reinterpret_cast<UploadContext*>(cls); |
- |
- if (0 != size) { |
- uc->fStream.write(data, size); |
- } |
- return MHD_YES; |
-} |
- |
-class UrlHandler { |
-public: |
- virtual ~UrlHandler() {} |
- virtual bool canHandle(const char* method, const char* url) = 0; |
- virtual int handle(Request* request, MHD_Connection* connection, |
- const char* url, const char* method, |
- const char* upload_data, size_t* upload_data_size) = 0; |
-}; |
- |
-class CmdHandler : public UrlHandler { |
-public: |
- bool canHandle(const char* method, const char* url) override { |
- const char* kBasePath = "/cmd"; |
- return 0 == strncmp(url, kBasePath, strlen(kBasePath)); |
- } |
- |
- int handle(Request* request, MHD_Connection* connection, |
- const char* url, const char* method, |
- const char* upload_data, size_t* upload_data_size) override { |
- SkTArray<SkString> commands; |
- SkStrSplit(url, "/", &commands); |
- |
- if (!request->fPicture.get() || commands.count() > 3) { |
- return MHD_NO; |
- } |
- |
- // /cmd or /cmd/N |
- if (0 == strcmp(method, MHD_HTTP_METHOD_GET)) { |
- int n; |
- if (commands.count() == 1) { |
- n = request->fDebugCanvas->getSize() - 1; |
- } else { |
- sscanf(commands[1].c_str(), "%d", &n); |
- } |
- return SendJSON(connection, request, n); |
- } |
- |
- // /cmd/N, for now only delete supported |
- if (commands.count() == 2 && 0 == strcmp(method, MHD_HTTP_METHOD_DELETE)) { |
- int n; |
- sscanf(commands[1].c_str(), "%d", &n); |
- request->fDebugCanvas->deleteDrawCommandAt(n); |
- return SendOK(connection); |
- } |
- |
- // /cmd/N/[0|1] |
- if (commands.count() == 3 && 0 == strcmp(method, MHD_HTTP_METHOD_POST)) { |
- int n, toggle; |
- sscanf(commands[1].c_str(), "%d", &n); |
- sscanf(commands[2].c_str(), "%d", &toggle); |
- request->fDebugCanvas->toggleCommand(n, toggle); |
- return SendOK(connection); |
- } |
- |
- return MHD_NO; |
- } |
-}; |
- |
-class ImgHandler : public UrlHandler { |
-public: |
- bool canHandle(const char* method, const char* url) override { |
- static const char* kBasePath = "/img"; |
- return 0 == strcmp(method, MHD_HTTP_METHOD_GET) && |
- 0 == strncmp(url, kBasePath, strlen(kBasePath)); |
- } |
- |
- int handle(Request* request, MHD_Connection* connection, |
- const char* url, const char* method, |
- const char* upload_data, size_t* upload_data_size) override { |
- SkTArray<SkString> commands; |
- SkStrSplit(url, "/", &commands); |
- |
- if (!request->fPicture.get() || commands.count() > 2) { |
- return MHD_NO; |
- } |
- |
- int n; |
- // /img or /img/N |
- if (commands.count() == 1) { |
- n = request->fDebugCanvas->getSize() - 1; |
- } else { |
- sscanf(commands[1].c_str(), "%d", &n); |
- } |
- |
- SkAutoTUnref<SkData> data(request->drawToPng(n)); |
- return SendData(connection, data, "image/png"); |
- } |
-}; |
- |
-class BreakHandler : public UrlHandler { |
-public: |
- bool canHandle(const char* method, const char* url) override { |
- static const char* kBasePath = "/break"; |
- return 0 == strcmp(method, MHD_HTTP_METHOD_GET) && |
- 0 == strncmp(url, kBasePath, strlen(kBasePath)); |
- } |
- |
- static SkColor GetPixel(Request* request, int x, int y) { |
- SkCanvas* canvas = request->getCanvas(); |
- canvas->flush(); |
- SkAutoTDelete<SkBitmap> bitmap(request->getBitmapFromCanvas(canvas)); |
- SkASSERT(bitmap); |
- bitmap->lockPixels(); |
- uint8_t* start = ((uint8_t*) bitmap->getPixels()) + (y * Request::kImageWidth + x) * 4; |
- SkColor result = SkColorSetARGB(start[3], start[0], start[1], start[2]); |
- bitmap->unlockPixels(); |
- return result; |
- } |
- |
- int handle(Request* request, MHD_Connection* connection, |
- const char* url, const char* method, |
- const char* upload_data, size_t* upload_data_size) override { |
- SkTArray<SkString> commands; |
- SkStrSplit(url, "/", &commands); |
- |
- if (!request->fPicture.get() || commands.count() != 4) { |
- return MHD_NO; |
- } |
- |
- // /break/<n>/<x>/<y> |
- int n; |
- sscanf(commands[1].c_str(), "%d", &n); |
- int x; |
- sscanf(commands[2].c_str(), "%d", &x); |
- int y; |
- sscanf(commands[3].c_str(), "%d", &y); |
- |
- int count = request->fDebugCanvas->getSize(); |
- SkASSERT(n < count); |
- |
- SkCanvas* canvas = request->getCanvas(); |
- canvas->clear(SK_ColorWHITE); |
- int saveCount = canvas->save(); |
- for (int i = 0; i <= n; ++i) { |
- request->fDebugCanvas->getDrawCommandAt(i)->execute(canvas); |
- } |
- SkColor target = GetPixel(request, x, y); |
- Json::Value response(Json::objectValue); |
- Json::Value startColor(Json::arrayValue); |
- startColor.append(Json::Value(SkColorGetR(target))); |
- startColor.append(Json::Value(SkColorGetG(target))); |
- startColor.append(Json::Value(SkColorGetB(target))); |
- startColor.append(Json::Value(SkColorGetA(target))); |
- response["startColor"] = startColor; |
- response["endColor"] = startColor; |
- response["endOp"] = Json::Value(n); |
- for (int i = n + 1; i < n + count; ++i) { |
- int index = i % count; |
- if (index == 0) { |
- // reset canvas for wraparound |
- canvas->restoreToCount(saveCount); |
- canvas->clear(SK_ColorWHITE); |
- saveCount = canvas->save(); |
- } |
- request->fDebugCanvas->getDrawCommandAt(index)->execute(canvas); |
- SkColor current = GetPixel(request, x, y); |
- if (current != target) { |
- Json::Value endColor(Json::arrayValue); |
- endColor.append(Json::Value(SkColorGetR(current))); |
- endColor.append(Json::Value(SkColorGetG(current))); |
- endColor.append(Json::Value(SkColorGetB(current))); |
- endColor.append(Json::Value(SkColorGetA(current))); |
- response["endColor"] = endColor; |
- response["endOp"] = Json::Value(index); |
- break; |
- } |
- } |
- canvas->restoreToCount(saveCount); |
- SkDynamicMemoryWStream stream; |
- stream.writeText(Json::FastWriter().write(response).c_str()); |
- SkAutoTUnref<SkData> data(stream.copyToData()); |
- return SendData(connection, data, "application/json"); |
- } |
-}; |
- |
-/** |
- Updates the clip visualization alpha. On all subsequent /img requests, the clip will be drawn in |
- black with the specified alpha. 0 = no visible clip, 255 = fully opaque clip. |
- */ |
-class ClipAlphaHandler : public UrlHandler { |
-public: |
- bool canHandle(const char* method, const char* url) override { |
- static const char* kBasePath = "/clipAlpha/"; |
- return 0 == strcmp(method, MHD_HTTP_METHOD_POST) && |
- 0 == strncmp(url, kBasePath, strlen(kBasePath)); |
- } |
- |
- int handle(Request* request, MHD_Connection* connection, |
- const char* url, const char* method, |
- const char* upload_data, size_t* upload_data_size) override { |
- SkTArray<SkString> commands; |
- SkStrSplit(url, "/", &commands); |
- |
- if (!request->fPicture.get() || commands.count() != 2) { |
- return MHD_NO; |
- } |
- |
- int alpha; |
- sscanf(commands[1].c_str(), "%d", &alpha); |
- |
- request->fDebugCanvas->setClipVizColor(SkColorSetARGB(alpha, 0, 0, 0)); |
- return SendOK(connection); |
- } |
-}; |
- |
-/** |
- Controls whether GPU rendering is enabled. Posting to /enableGPU/1 turns GPU on, /enableGPU/0 |
- disables it. |
- */ |
-class EnableGPUHandler : public UrlHandler { |
-public: |
- bool canHandle(const char* method, const char* url) override { |
- static const char* kBasePath = "/enableGPU/"; |
- return 0 == strcmp(method, MHD_HTTP_METHOD_POST) && |
- 0 == strncmp(url, kBasePath, strlen(kBasePath)); |
- } |
- |
- int handle(Request* request, MHD_Connection* connection, |
- const char* url, const char* method, |
- const char* upload_data, size_t* upload_data_size) override { |
- SkTArray<SkString> commands; |
- SkStrSplit(url, "/", &commands); |
- |
- if (commands.count() != 2) { |
- return MHD_NO; |
- } |
- |
- int enable; |
- sscanf(commands[1].c_str(), "%d", &enable); |
- |
- if (enable) { |
- SkSurface* surface = request->createGPUSurface(); |
- if (surface) { |
- request->fSurface.reset(surface); |
- request->fGPUEnabled = true; |
- return SendOK(connection); |
- } |
- return SendError(connection, "Unable to create GPU surface"); |
- } |
- request->fSurface.reset(request->createCPUSurface()); |
- request->fGPUEnabled = false; |
- return SendOK(connection); |
- } |
-}; |
- |
-class PostHandler : public UrlHandler { |
-public: |
- bool canHandle(const char* method, const char* url) override { |
- return 0 == strcmp(method, MHD_HTTP_METHOD_POST) && |
- 0 == strcmp(url, "/new"); |
- } |
- |
- int handle(Request* request, MHD_Connection* connection, |
- const char* url, const char* method, |
- const char* upload_data, size_t* upload_data_size) override { |
- UploadContext* uc = request->fUploadContext; |
- |
- // New connection |
- if (!uc) { |
- // TODO make this a method on request |
- uc = new UploadContext; |
- uc->connection = connection; |
- uc->fPostProcessor = MHD_create_post_processor(connection, kBufferSize, |
- &process_upload_data, uc); |
- SkASSERT(uc->fPostProcessor); |
- |
- request->fUploadContext = uc; |
- return MHD_YES; |
- } |
- |
- // in process upload |
- if (0 != *upload_data_size) { |
- SkASSERT(uc->fPostProcessor); |
- MHD_post_process(uc->fPostProcessor, upload_data, *upload_data_size); |
- *upload_data_size = 0; |
- return MHD_YES; |
- } |
- |
- // end of upload |
- MHD_destroy_post_processor(uc->fPostProcessor); |
- uc->fPostProcessor = nullptr; |
- |
- // parse picture from stream |
- request->fPicture.reset( |
- SkPicture::CreateFromStream(request->fUploadContext->fStream.detachAsStream())); |
- if (!request->fPicture.get()) { |
- fprintf(stderr, "Could not create picture from stream.\n"); |
- return MHD_NO; |
- } |
- |
- // pour picture into debug canvas |
- request->fDebugCanvas.reset(new SkDebugCanvas(Request::kImageWidth, |
- Request::kImageHeight)); |
- request->fDebugCanvas->drawPicture(request->fPicture); |
- |
- // clear upload context |
- delete request->fUploadContext; |
- request->fUploadContext = nullptr; |
- |
- return SendTemplate(connection, true, "/"); |
- } |
-}; |
- |
-class DownloadHandler : public UrlHandler { |
-public: |
- bool canHandle(const char* method, const char* url) override { |
- return 0 == strcmp(method, MHD_HTTP_METHOD_GET) && |
- 0 == strcmp(url, "/download"); |
- } |
- |
- int handle(Request* request, MHD_Connection* connection, |
- const char* url, const char* method, |
- const char* upload_data, size_t* upload_data_size) override { |
- if (!request->fPicture.get()) { |
- return MHD_NO; |
- } |
- |
- // TODO move to a function |
- // Playback into picture recorder |
- SkPictureRecorder recorder; |
- SkCanvas* canvas = recorder.beginRecording(Request::kImageWidth, |
- Request::kImageHeight); |
- |
- request->fDebugCanvas->draw(canvas); |
- |
- SkAutoTUnref<SkPicture> picture(recorder.endRecording()); |
- |
- SkDynamicMemoryWStream outStream; |
- |
- SkAutoTUnref<SkPixelSerializer> serializer(SkImageEncoder::CreatePixelSerializer()); |
- picture->serialize(&outStream, serializer); |
- |
- SkAutoTUnref<SkData> data(outStream.copyToData()); |
- |
- // TODO fancier name handling |
- return SendData(connection, data, "application/octet-stream", true, |
- "attachment; filename=something.skp;"); |
- } |
-}; |
- |
-class InfoHandler : public UrlHandler { |
-public: |
- bool canHandle(const char* method, const char* url) override { |
- const char* kBaseName = "/info"; |
- return 0 == strcmp(method, MHD_HTTP_METHOD_GET) && |
- 0 == strncmp(url, kBaseName, strlen(kBaseName)); |
- } |
- |
- int handle(Request* request, MHD_Connection* connection, |
- const char* url, const char* method, |
- const char* upload_data, size_t* upload_data_size) override { |
- SkTArray<SkString> commands; |
- SkStrSplit(url, "/", &commands); |
- |
- if (!request->fPicture.get() || commands.count() > 2) { |
- return MHD_NO; |
- } |
- |
- // drawTo |
- SkAutoTUnref<SkSurface> surface(request->createCPUSurface()); |
- SkCanvas* canvas = surface->getCanvas(); |
- |
- int n; |
- // /info or /info/N |
- if (commands.count() == 1) { |
- n = request->fDebugCanvas->getSize() - 1; |
- } else { |
- sscanf(commands[1].c_str(), "%d", &n); |
- } |
- |
- // TODO this is really slow and we should cache the matrix and clip |
- request->fDebugCanvas->drawTo(canvas, n); |
- |
- // make some json |
- SkMatrix vm = request->fDebugCanvas->getCurrentMatrix(); |
- SkIRect clip = request->fDebugCanvas->getCurrentClip(); |
- Json::Value info(Json::objectValue); |
- info["ViewMatrix"] = SkJSONCanvas::MakeMatrix(vm); |
- info["ClipRect"] = SkJSONCanvas::MakeIRect(clip); |
- |
- std::string json = Json::FastWriter().write(info); |
- |
- // We don't want the null terminator so strlen is correct |
- SkAutoTUnref<SkData> data(SkData::NewWithCopy(json.c_str(), strlen(json.c_str()))); |
- return SendData(connection, data, "application/json"); |
- } |
-}; |
- |
-class DataHandler : public UrlHandler { |
-public: |
- bool canHandle(const char* method, const char* url) override { |
- static const char* kBaseUrl = "/data"; |
- return 0 == strcmp(method, MHD_HTTP_METHOD_GET) && |
- 0 == strncmp(url, kBaseUrl, strlen(kBaseUrl)); |
- } |
- |
- int handle(Request* request, MHD_Connection* connection, |
- const char* url, const char* method, |
- const char* upload_data, size_t* upload_data_size) override { |
- SkTArray<SkString> commands; |
- SkStrSplit(url, "/", &commands); |
- |
- if (!request->fPicture.get() || commands.count() != 2) { |
- return MHD_NO; |
- } |
- |
- SkAutoTUnref<UrlDataManager::UrlData> urlData( |
- SkRef(request->fUrlDataManager.getDataFromUrl(SkString(url)))); |
- |
- if (urlData) { |
- return SendData(connection, urlData->fData.get(), urlData->fContentType.c_str()); |
- } |
- return MHD_NO; |
- } |
-}; |
- |
-class RootHandler : public UrlHandler { |
-public: |
- bool canHandle(const char* method, const char* url) override { |
- return 0 == strcmp(method, MHD_HTTP_METHOD_GET) && |
- 0 == strcmp(url, "/"); |
- } |
- |
- int handle(Request* request, MHD_Connection* connection, |
- const char* url, const char* method, |
- const char* upload_data, size_t* upload_data_size) override { |
- return SendTemplate(connection); |
- } |
-}; |
- |
class UrlManager { |
public: |
UrlManager() { |