Create image cache for use by json canvas
authorjoshualitt <joshualitt@chromium.org>
Mon, 8 Feb 2016 15:08:21 +0000 (07:08 -0800)
committerCommit bot <commit-bot@chromium.org>
Mon, 8 Feb 2016 15:08:21 +0000 (07:08 -0800)
BUG=skia:
GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1670153005

Review URL: https://codereview.chromium.org/1670153005

gyp/skiaserve.gyp
gyp/tools.gyp
tools/UrlDataManager.cpp [new file with mode: 0644]
tools/UrlDataManager.h [new file with mode: 0644]
tools/skiaserve/skiaserve.cpp

index c133762..58fdea1 100644 (file)
@@ -40,6 +40,7 @@
         'tools.gyp:crash_handler',
         'tools.gyp:proc_stats',
         'tools.gyp:resources',
+        'tools.gyp:url_data_manager',
       ],
     },
   ],
index b585e16..e303669 100644 (file)
       },
     },
     {
+      'target_name': 'url_data_manager',
+      'type': 'static_library',
+      'sources': [
+        '../tools/UrlDataManager.h',
+        '../tools/UrlDataManager.cpp',
+      ],
+      'dependencies': [
+        'skia_lib.gyp:skia_lib',
+      ],
+      'include_dirs': [
+         '../include/private',
+         '../src/core',
+      ],
+      'direct_dependent_settings': {
+        'include_dirs': [
+          '../include/private',
+          '../tools', 
+        ],
+      },
+    },
+    {
       'target_name': 'whitelist_typefaces',
       'type': 'executable',
       'sources': [
diff --git a/tools/UrlDataManager.cpp b/tools/UrlDataManager.cpp
new file mode 100644 (file)
index 0000000..a30d8ba
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "UrlDataManager.h"
+
+bool operator==(const SkData& a, const SkData& b) {
+    return a.equals(&b);
+}
+
+UrlDataManager::UrlDataManager(SkString rootUrl) : fRootUrl(rootUrl), fDataId(0) {}
+
+SkString UrlDataManager::addData(SkData* data, const char* contentType) {
+    UrlData* urlData = fCache.find(*data);
+    if (fCache.find(*data)) {
+        SkASSERT(data->equals(urlData->fData.get()));
+        return urlData->fUrl;
+    }
+
+    urlData = new UrlData;
+    urlData->fData.reset(SkRef(data));
+    urlData->fContentType.set(contentType);
+    urlData->fUrl.appendf("%s/%d", fRootUrl.c_str(), fDataId++);
+
+    fCache.add(urlData);
+
+    SkASSERT(!fUrlLookup.find(urlData->fUrl));
+    fUrlLookup.add(urlData);
+    return urlData->fUrl;
+}
+
+void UrlDataManager::reset() {
+    SkTDynamicHash<UrlData, SkData, LookupTrait>::Iter iter(&fCache);
+    while (!iter.done()) {
+        UrlData* urlData = &(*iter);
+        urlData->unref();
+        ++iter;
+    }
+
+    fCache.rewind();
+}
diff --git a/tools/UrlDataManager.h b/tools/UrlDataManager.h
new file mode 100644 (file)
index 0000000..b047eda
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkUrlDataManager_DEFINED
+#define SkUrlDataManager_DEFINED
+
+#include "SkChecksum.h"
+#include "SkData.h"
+#include "SkString.h"
+#include "SkTDynamicHash.h"
+
+/*
+ * A simple class which allows clients to add opaque data types, and returns a url where this data
+ * will be hosted.  Its up to the owner of this class to actually serve the data.
+ */
+bool operator==(const SkData& a, const SkData& b);
+
+class UrlDataManager {
+public:
+    UrlDataManager(SkString rootUrl);
+    ~UrlDataManager() { this->reset(); }
+
+    /*
+     * Adds a data blob to the cache with a particular content type.  UrlDataManager will hash
+     * the blob data to ensure uniqueness
+     */
+    SkString addData(SkData*, const char* contentType);
+
+    struct UrlData : public SkRefCnt {
+        SkString fUrl;
+        SkString fContentType;
+        SkAutoTUnref<SkData> fData;
+    };
+
+    /*
+     * returns the UrlData object which should be hosted at 'url'
+     */
+    UrlData* getDataFromUrl(SkString url) {
+        return fUrlLookup.find(url);
+    }
+    void reset();
+
+private:
+    struct LookupTrait {
+        // We use the data as a hash, this is not really optimal but is fine until proven otherwise
+        static const SkData& GetKey(const UrlData& data) {
+            return *data.fData.get();
+        }
+
+        static uint32_t Hash(const SkData& key) {
+            return SkChecksum::Murmur3(key.bytes(), key.size());
+        }
+    };
+
+    struct ReverseLookupTrait {
+        static const SkString& GetKey(const UrlData& data) {
+            return data.fUrl;
+        }
+
+        static uint32_t Hash(const SkString& key) {
+            return SkChecksum::Murmur3(key.c_str(), strlen(key.c_str()));
+        }
+    };
+
+
+    SkString fRootUrl;
+    SkTDynamicHash<UrlData, SkData, LookupTrait> fCache;
+    SkTDynamicHash<UrlData, SkString, ReverseLookupTrait> fUrlLookup;
+    uint32_t fDataId;
+};
+
+#endif
index 85e9c87..a8ddcdf 100644 (file)
@@ -18,6 +18,8 @@
 #include "SkStream.h"
 #include "SkSurface.h"
 
+#include "UrlDataManager.h"
+
 #include <sys/socket.h>
 #include <microhttpd.h>
 
@@ -63,10 +65,11 @@ struct UploadContext {
 };
 
 struct Request {
-    Request() : fUploadContext(nullptr) {}
+    Request(SkString rootUrl) : fUploadContext(nullptr), fUrlDataManager(rootUrl) {}
     UploadContext* fUploadContext;
     SkAutoTUnref<SkPicture> fPicture;
     SkAutoTUnref<SkDebugCanvas> fDebugCanvas;
+    UrlDataManager fUrlDataManager;
 };
 
 // TODO factor this out into functions, also handle CPU path
@@ -99,6 +102,7 @@ SkData* writeCanvasToPng(SkCanvas* canvas) {
     }
 
     // write to png
+    // TODO encoding to png can be quite slow, we should investigate bmp
     SkData* png = SkImageEncoder::EncodeData(bmp, SkImageEncoder::kPNG_Type, 100);
     if (!png) {
         fprintf(stderr, "Can't encode to png\n");
@@ -411,6 +415,33 @@ public:
     }
 };
 
+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:
@@ -436,6 +467,7 @@ public:
         fHandlers.push_back(new CmdHandler);
         fHandlers.push_back(new InfoHandler);
         fHandlers.push_back(new DownloadHandler);
+        fHandlers.push_back(new DataHandler);
     }
 
     ~UrlManager() {
@@ -476,7 +508,7 @@ int answer_to_connection(void* cls, struct MHD_Connection* connection,
 }
 
 int skiaserve_main() {
-    Request request; // This simple server has one request
+    Request request(SkString("/data")); // This simple server has one request
     struct MHD_Daemon* daemon;
     // TODO Add option to bind this strictly to an address, e.g. localhost, for security.
     daemon = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, FLAGS_port, nullptr, nullptr,