summaryrefslogtreecommitdiff
path: root/platform/default
diff options
context:
space:
mode:
Diffstat (limited to 'platform/default')
-rw-r--r--platform/default/application_root.cpp28
-rw-r--r--platform/default/asset_file_source.cpp15
-rw-r--r--platform/default/default_file_source.cpp69
-rw-r--r--platform/default/glfw_view.cpp191
-rw-r--r--platform/default/headless_display.cpp1
-rw-r--r--platform/default/headless_view.cpp260
-rw-r--r--platform/default/headless_view_glx.cpp128
-rw-r--r--platform/default/http_file_source.cpp (renamed from platform/default/http_request_curl.cpp)263
-rw-r--r--platform/default/image.cpp16
-rw-r--r--platform/default/mbgl/storage/offline.cpp14
-rw-r--r--platform/default/mbgl/storage/offline_database.cpp84
-rw-r--r--platform/default/mbgl/storage/offline_database.hpp8
-rw-r--r--platform/default/mbgl/storage/offline_download.cpp52
-rw-r--r--platform/default/mbgl/storage/offline_download.hpp25
-rw-r--r--platform/default/online_file_source.cpp156
-rw-r--r--platform/default/png_reader.cpp4
-rw-r--r--platform/default/sqlite3.cpp66
-rw-r--r--platform/default/sqlite3.hpp24
-rw-r--r--platform/default/webp_reader.cpp7
19 files changed, 601 insertions, 810 deletions
diff --git a/platform/default/application_root.cpp b/platform/default/application_root.cpp
deleted file mode 100644
index 6669a049a4..0000000000
--- a/platform/default/application_root.cpp
+++ /dev/null
@@ -1,28 +0,0 @@
-#include <mbgl/platform/platform.hpp>
-
-#include <uv.h>
-#include <libgen.h>
-
-namespace mbgl {
-namespace platform {
-
-// Returns the path to the root folder of the application.
-const std::string &applicationRoot() {
- static const std::string root = []() -> std::string {
- size_t max = 0;
- std::string dir;
- do {
- // Gradually increase the length of the string in case the path was truncated.
- max += 256;
- dir.resize(max);
- uv_exepath(const_cast<char *>(dir.data()), &max);
- } while (max == dir.size());
- dir.resize(max - 1);
- dir = dirname(const_cast<char *>(dir.c_str()));
- return dir;
- }();
- return root;
-}
-
-}
-}
diff --git a/platform/default/asset_file_source.cpp b/platform/default/asset_file_source.cpp
index 2573966c72..3a47f349fe 100644
--- a/platform/default/asset_file_source.cpp
+++ b/platform/default/asset_file_source.cpp
@@ -12,15 +12,6 @@
namespace mbgl {
-class AssetFileRequest : public FileRequest {
-public:
- AssetFileRequest(std::unique_ptr<WorkRequest> workRequest_)
- : workRequest(std::move(workRequest_)) {
- }
-
- std::unique_ptr<WorkRequest> workRequest;
-};
-
class AssetFileSource::Impl {
public:
Impl(const std::string& root_)
@@ -66,14 +57,14 @@ private:
AssetFileSource::AssetFileSource(const std::string& root)
: thread(std::make_unique<util::Thread<Impl>>(
- util::ThreadContext{"AssetFileSource", util::ThreadType::Worker, util::ThreadPriority::Regular},
+ util::ThreadContext{"AssetFileSource"},
root)) {
}
AssetFileSource::~AssetFileSource() = default;
-std::unique_ptr<FileRequest> AssetFileSource::request(const Resource& resource, Callback callback) {
- return std::make_unique<AssetFileRequest>(thread->invokeWithCallback(&Impl::request, callback, resource.url));
+std::unique_ptr<AsyncRequest> AssetFileSource::request(const Resource& resource, Callback callback) {
+ return thread->invokeWithCallback(&Impl::request, callback, resource.url);
}
} // namespace mbgl
diff --git a/platform/default/default_file_source.cpp b/platform/default/default_file_source.cpp
index 36839b2381..9d369210f8 100644
--- a/platform/default/default_file_source.cpp
+++ b/platform/default/default_file_source.cpp
@@ -25,29 +25,6 @@ namespace mbgl {
class DefaultFileSource::Impl {
public:
- class Task {
- public:
- Task(Resource resource, FileSource::Callback callback, DefaultFileSource::Impl* impl) {
- auto offlineResponse = impl->offlineDatabase.get(resource);
-
- Resource revalidation = resource;
-
- if (offlineResponse) {
- revalidation.priorModified = offlineResponse->modified;
- revalidation.priorExpires = offlineResponse->expires;
- revalidation.priorEtag = offlineResponse->etag;
- callback(*offlineResponse);
- }
-
- onlineRequest = impl->onlineFileSource.request(revalidation, [=] (Response onlineResponse) {
- impl->offlineDatabase.put(revalidation, onlineResponse);
- callback(onlineResponse);
- });
- }
-
- std::unique_ptr<FileRequest> onlineRequest;
- };
-
Impl(const std::string& cachePath, uint64_t maximumCacheSize)
: offlineDatabase(cachePath, maximumCacheSize) {
}
@@ -104,11 +81,39 @@ public:
getDownload(regionID).setState(state);
}
- void request(FileRequest* req, Resource resource, Callback callback) {
- tasks[req] = std::make_unique<Task>(resource, callback, this);
+ void request(AsyncRequest* req, Resource resource, Callback callback) {
+ Resource revalidation = resource;
+
+ const bool hasPrior = resource.priorEtag || resource.priorModified || resource.priorExpires;
+ if (!hasPrior || resource.necessity == Resource::Optional) {
+ auto offlineResponse = offlineDatabase.get(resource);
+
+ if (resource.necessity == Resource::Optional && !offlineResponse) {
+ // Ensure there's always a response that we can send, so the caller knows that
+ // there's no optional data available in the cache.
+ offlineResponse.emplace();
+ offlineResponse->noContent = true;
+ offlineResponse->error = std::make_unique<Response::Error>(
+ Response::Error::Reason::NotFound, "Not found in offline database");
+ }
+
+ if (offlineResponse) {
+ revalidation.priorModified = offlineResponse->modified;
+ revalidation.priorExpires = offlineResponse->expires;
+ revalidation.priorEtag = offlineResponse->etag;
+ callback(*offlineResponse);
+ }
+ }
+
+ if (resource.necessity == Resource::Required) {
+ tasks[req] = onlineFileSource.request(revalidation, [=] (Response onlineResponse) {
+ this->offlineDatabase.put(revalidation, onlineResponse);
+ callback(onlineResponse);
+ });
+ }
}
- void cancel(FileRequest* req) {
+ void cancel(AsyncRequest* req) {
tasks.erase(req);
}
@@ -132,14 +137,14 @@ private:
OfflineDatabase offlineDatabase;
OnlineFileSource onlineFileSource;
- std::unordered_map<FileRequest*, std::unique_ptr<Task>> tasks;
+ std::unordered_map<AsyncRequest*, std::unique_ptr<AsyncRequest>> tasks;
std::unordered_map<int64_t, std::unique_ptr<OfflineDownload>> downloads;
};
DefaultFileSource::DefaultFileSource(const std::string& cachePath,
const std::string& assetRoot,
uint64_t maximumCacheSize)
- : thread(std::make_unique<util::Thread<Impl>>(util::ThreadContext{"DefaultFileSource", util::ThreadType::Unknown, util::ThreadPriority::Low},
+ : thread(std::make_unique<util::Thread<Impl>>(util::ThreadContext{"DefaultFileSource", util::ThreadPriority::Low},
cachePath, maximumCacheSize)),
assetFileSource(std::make_unique<AssetFileSource>(assetRoot)) {
}
@@ -154,20 +159,20 @@ std::string DefaultFileSource::getAccessToken() const {
return thread->invokeSync<std::string>(&Impl::getAccessToken);
}
-std::unique_ptr<FileRequest> DefaultFileSource::request(const Resource& resource, Callback callback) {
- class DefaultFileRequest : public FileRequest {
+std::unique_ptr<AsyncRequest> DefaultFileSource::request(const Resource& resource, Callback callback) {
+ class DefaultFileRequest : public AsyncRequest {
public:
DefaultFileRequest(Resource resource_, FileSource::Callback callback_, util::Thread<DefaultFileSource::Impl>& thread_)
: thread(thread_),
workRequest(thread.invokeWithCallback(&DefaultFileSource::Impl::request, callback_, this, resource_)) {
}
- ~DefaultFileRequest() {
+ ~DefaultFileRequest() override {
thread.invoke(&DefaultFileSource::Impl::cancel, this);
}
util::Thread<DefaultFileSource::Impl>& thread;
- std::unique_ptr<WorkRequest> workRequest;
+ std::unique_ptr<AsyncRequest> workRequest;
};
if (isAssetURL(resource.url)) {
diff --git a/platform/default/glfw_view.cpp b/platform/default/glfw_view.cpp
index 790b0a4dd7..67e9748a70 100644
--- a/platform/default/glfw_view.cpp
+++ b/platform/default/glfw_view.cpp
@@ -1,7 +1,7 @@
#include <mbgl/platform/default/glfw_view.hpp>
-#include <mbgl/annotation/point_annotation.hpp>
-#include <mbgl/annotation/shape_annotation.hpp>
+#include <mbgl/annotation/annotation.hpp>
#include <mbgl/sprite/sprite_image.hpp>
+#include <mbgl/style/transition_options.hpp>
#include <mbgl/gl/gl.hpp>
#include <mbgl/gl/gl_values.hpp>
#include <mbgl/gl/gl_helper.hpp>
@@ -9,6 +9,7 @@
#include <mbgl/platform/platform.hpp>
#include <mbgl/util/string.hpp>
#include <mbgl/util/chrono.hpp>
+#include <mbgl/map/camera.hpp>
#include <cassert>
#include <cstdlib>
@@ -94,6 +95,8 @@ GLFWView::GLFWView(bool fullscreen_, bool benchmark_)
printf("- Press `N` to reset north\n");
printf("- Press `R` to toggle any available `night` style class\n");
printf("- Press `Z` to cycle through north orientations\n");
+ printf("- Prezz `X` to cycle through the viewport modes\n");
+ printf("- Press `A` to cycle through Mapbox offices in the world + dateline monument\n");
printf("\n");
printf("- Press `1` through `6` to add increasing numbers of point annotations for testing\n");
printf("- Press `7` through `0` to add increasing numbers of shape annotations for testing\n");
@@ -137,20 +140,17 @@ void GLFWView::onKey(GLFWwindow *window, int key, int /*scancode*/, int action,
if (!mods)
view->map->resetPosition();
break;
- case GLFW_KEY_C:
- view->toggleClipMasks();
- break;
case GLFW_KEY_S:
if (view->changeStyleCallback)
view->changeStyleCallback();
break;
case GLFW_KEY_R:
if (!mods) {
- view->map->setDefaultTransitionDuration(mbgl::Milliseconds(300));
+ static const mbgl::style::TransitionOptions transition { { mbgl::Milliseconds(300) } };
if (view->map->hasClass("night")) {
- view->map->removeClass("night");
+ view->map->removeClass("night", transition);
} else {
- view->map->addClass("night");
+ view->map->addClass("night", transition);
}
}
break;
@@ -167,6 +167,26 @@ void GLFWView::onKey(GLFWwindow *window, int key, int /*scancode*/, int action,
case GLFW_KEY_P: {
view->addRandomCustomPointAnnotations(1);
} break;
+ case GLFW_KEY_A: {
+ // XXX Fix precision loss in flyTo:
+ // https://github.com/mapbox/mapbox-gl-native/issues/4298
+ static const std::vector<mbgl::LatLng> places = {
+ mbgl::LatLng { -16.796665, -179.999983 }, // Dateline monument
+ mbgl::LatLng { 12.9810542, 77.6345551 }, // Mapbox Bengaluru, India
+ mbgl::LatLng { -13.15607,-74.21773 }, // Mapbox Peru
+ mbgl::LatLng { 37.77572, -122.4158818 }, // Mapbox SF, USA
+ mbgl::LatLng { 38.91318,-77.03255 }, // Mapbox DC, USA
+ };
+ static size_t nextPlace = 0;
+ mbgl::CameraOptions cameraOptions;
+ cameraOptions.center = places[nextPlace++];
+ cameraOptions.zoom = 20;
+ cameraOptions.pitch = 30;
+
+ mbgl::AnimationOptions animationOptions(mbgl::Seconds(10));
+ view->map->flyTo(cameraOptions, animationOptions);
+ nextPlace = nextPlace % places.size();
+ } break;
}
}
@@ -187,14 +207,11 @@ void GLFWView::onKey(GLFWwindow *window, int key, int /*scancode*/, int action,
}
}
-mbgl::LatLng GLFWView::makeRandomPoint() const {
- const auto nw = map->latLngForPixel({ 0, 0 });
- const auto se = map->latLngForPixel({ double(width), double(height) });
-
- const double lon = nw.longitude + (se.longitude - nw.longitude) * (double(std::rand()) / RAND_MAX);
- const double lat = se.latitude + (nw.latitude - se.latitude) * (double(std::rand()) / RAND_MAX);
-
- return { lat, lon };
+mbgl::Point<double> GLFWView::makeRandomPoint() const {
+ const double x = width * double(std::rand()) / RAND_MAX;
+ const double y = height * double(std::rand()) / RAND_MAX;
+ mbgl::LatLng latLng = map->latLngForPixel({ x, y });
+ return { latLng.longitude, latLng.latitude };
}
std::shared_ptr<const mbgl::SpriteImage>
@@ -236,65 +253,35 @@ void GLFWView::nextOrientation() {
}
}
-void GLFWView::toggleClipMasks() {
- showClipMasks = !showClipMasks;
- map->update(mbgl::Update::Repaint);
-}
-
void GLFWView::addRandomCustomPointAnnotations(int count) {
- std::vector<mbgl::PointAnnotation> points;
-
for (int i = 0; i < count; i++) {
static int spriteID = 1;
const auto name = std::string{ "marker-" } + mbgl::util::toString(spriteID++);
map->addAnnotationIcon(name, makeSpriteImage(22, 22, 1));
spriteIDs.push_back(name);
- points.emplace_back(makeRandomPoint(), name);
+ annotationIDs.push_back(map->addAnnotation(mbgl::SymbolAnnotation { makeRandomPoint(), name }));
}
-
- auto newIDs = map->addPointAnnotations(points);
- annotationIDs.insert(annotationIDs.end(), newIDs.begin(), newIDs.end());
}
void GLFWView::addRandomPointAnnotations(int count) {
- std::vector<mbgl::PointAnnotation> points;
-
for (int i = 0; i < count; i++) {
- points.emplace_back(makeRandomPoint(), "default_marker");
+ annotationIDs.push_back(map->addAnnotation(mbgl::SymbolAnnotation { makeRandomPoint(), "default_marker" }));
}
-
- auto newIDs = map->addPointAnnotations(points);
- annotationIDs.insert(annotationIDs.end(), newIDs.begin(), newIDs.end());
}
void GLFWView::addRandomShapeAnnotations(int count) {
- std::vector<mbgl::ShapeAnnotation> shapes;
-
- mbgl::FillAnnotationProperties properties;
- properties.opacity = .1;
-
for (int i = 0; i < count; i++) {
- mbgl::AnnotationSegment triangle;
- triangle.push_back(makeRandomPoint());
- triangle.push_back(makeRandomPoint());
- triangle.push_back(makeRandomPoint());
-
- mbgl::AnnotationSegments segments;
- segments.push_back(triangle);
-
- shapes.emplace_back(segments, properties);
+ mbgl::Polygon<double> triangle;
+ triangle.push_back({ makeRandomPoint(), makeRandomPoint(), makeRandomPoint() });
+ annotationIDs.push_back(map->addAnnotation(mbgl::FillAnnotation { triangle, .1 }));
}
-
- auto newIDs = map->addShapeAnnotations(shapes);
- annotationIDs.insert(annotationIDs.end(), newIDs.begin(), newIDs.end());
}
void GLFWView::clearAnnotations() {
- if (annotationIDs.empty()) {
- return;
+ for (const auto& id : annotationIDs) {
+ map->removeAnnotation(id);
}
- map->removeAnnotations(annotationIDs);
annotationIDs.clear();
}
@@ -326,7 +313,7 @@ void GLFWView::onScroll(GLFWwindow *window, double /*xOffset*/, double yOffset)
scale = 1.0 / scale;
}
- view->map->scaleBy(scale, { view->lastX, view->lastY });
+ view->map->scaleBy(scale, mbgl::ScreenCoordinate { view->lastX, view->lastY });
}
void GLFWView::onWindowResize(GLFWwindow *window, int width, int height) {
@@ -363,9 +350,9 @@ void GLFWView::onMouseClick(GLFWwindow *window, int button, int action, int modi
double now = glfwGetTime();
if (now - view->lastClick < 0.4 /* ms */) {
if (modifiers & GLFW_MOD_SHIFT) {
- view->map->scaleBy(0.5, { view->lastX, view->lastY }, mbgl::Milliseconds(500));
+ view->map->scaleBy(0.5, mbgl::ScreenCoordinate { view->lastX, view->lastY }, mbgl::Milliseconds(500));
} else {
- view->map->scaleBy(2.0, { view->lastX, view->lastY }, mbgl::Milliseconds(500));
+ view->map->scaleBy(2.0, mbgl::ScreenCoordinate { view->lastX, view->lastY }, mbgl::Milliseconds(500));
}
}
view->lastClick = now;
@@ -396,18 +383,39 @@ void GLFWView::onMouseMove(GLFWwindow *window, double x, double y) {
}
void GLFWView::run() {
- while (!glfwWindowShouldClose(window)) {
- glfwWaitEvents();
- const bool dirty = !clean.test_and_set();
+ auto callback = [&] {
+ if (glfwWindowShouldClose(window)) {
+ runLoop.stop();
+ return;
+ }
+
+ glfwPollEvents();
+
if (dirty) {
const double started = glfwGetTime();
- map->renderSync();
+
+ glfwMakeContextCurrent(window);
+ glViewport(0, 0, fbWidth, fbHeight);
+
+ map->render();
+
+ glfwSwapBuffers(window);
+
report(1000 * (glfwGetTime() - started));
if (benchmark) {
map->update(mbgl::Update::Repaint);
}
+
+ dirty = false;
}
- }
+ };
+
+ frameTick.start(mbgl::Duration::zero(), mbgl::Milliseconds(1000 / 60), callback);
+#if defined(__APPLE__)
+ while (!glfwWindowShouldClose(window)) runLoop.run();
+#else
+ runLoop.run();
+#endif
}
float GLFWView::getPixelRatio() const {
@@ -430,70 +438,11 @@ void GLFWView::deactivate() {
glfwMakeContextCurrent(nullptr);
}
-void GLFWView::notify() {
- glfwPostEmptyEvent();
-}
-
void GLFWView::invalidate() {
- clean.clear();
+ dirty = true;
glfwPostEmptyEvent();
}
-void GLFWView::beforeRender() {
- // This is called from the map thread but `width` and `height`
- // can be accessed with no race because the main thread is blocked
- // when we render. This will be more straightforward when we move
- // rendering to the main thread.
- glViewport(0, 0, fbWidth, fbHeight);
-}
-
-void GLFWView::afterRender() {
- if (showClipMasks) {
- renderClipMasks();
- }
-
- glfwSwapBuffers(window);
-}
-
-void GLFWView::renderClipMasks() {
- // Read the stencil buffer
- auto pixels = std::make_unique<uint8_t[]>(fbWidth * fbHeight);
- glReadPixels(0, // GLint x
- 0, // GLint y
- fbWidth, // GLsizei width
- fbHeight, // GLsizei height
- GL_STENCIL_INDEX, // GLenum format
- GL_UNSIGNED_BYTE, // GLenum type
- pixels.get() // GLvoid * data
- );
-
- // Scale the Stencil buffer to cover the entire color space.
- auto it = pixels.get();
- auto end = it + fbWidth * fbHeight;
- const auto factor = 255.0f / *std::max_element(it, end);
- for (; it != end; ++it) {
- *it *= factor;
- }
-
- using namespace mbgl::gl;
- Preserve<PixelZoom> pixelZoom;
- Preserve<RasterPos> rasterPos;
- Preserve<StencilTest> stencilTest;
- Preserve<DepthTest> depthTest;
- Preserve<Program> program;
- Preserve<ColorMask> colorMask;
-
- MBGL_CHECK_ERROR(glPixelZoom(1.0f, 1.0f));
- MBGL_CHECK_ERROR(glRasterPos2f(-1.0f, -1.0f));
- MBGL_CHECK_ERROR(glDisable(GL_STENCIL_TEST));
- MBGL_CHECK_ERROR(glDisable(GL_DEPTH_TEST));
- MBGL_CHECK_ERROR(glUseProgram(0));
- MBGL_CHECK_ERROR(glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE));
- MBGL_CHECK_ERROR(glWindowPos2i(0, 0));
-
- MBGL_CHECK_ERROR(glDrawPixels(fbWidth, fbHeight, GL_LUMINANCE, GL_UNSIGNED_BYTE, pixels.get()));
-}
-
void GLFWView::report(float duration) {
frames++;
frameTime += duration;
diff --git a/platform/default/headless_display.cpp b/platform/default/headless_display.cpp
index fccc91c727..8b9f3fe04b 100644
--- a/platform/default/headless_display.cpp
+++ b/platform/default/headless_display.cpp
@@ -16,7 +16,6 @@ HeadlessDisplay::HeadlessDisplay() {
CGLPixelFormatAttribute attributes[] = {
kCGLPFAOpenGLProfile,
static_cast<CGLPixelFormatAttribute>(kCGLOGLPVersion_Legacy),
- kCGLPFAAccelerated,
static_cast<CGLPixelFormatAttribute>(0)
};
diff --git a/platform/default/headless_view.cpp b/platform/default/headless_view.cpp
index 7fd3c37f3a..13ea78a709 100644
--- a/platform/default/headless_view.cpp
+++ b/platform/default/headless_view.cpp
@@ -1,163 +1,34 @@
#include <mbgl/platform/default/headless_view.hpp>
#include <mbgl/platform/default/headless_display.hpp>
-#include <mbgl/platform/log.hpp>
-#include <stdexcept>
-#include <sstream>
-#include <string>
-#include <cstring>
#include <cassert>
-#include <utility>
-
-#ifdef MBGL_USE_CGL
-#include <CoreFoundation/CoreFoundation.h>
-#elif MBGL_USE_GLX
-#include <GL/glx.h>
-#endif
+#include <cstring>
namespace mbgl {
HeadlessView::HeadlessView(float pixelRatio_, uint16_t width, uint16_t height)
- : display(std::make_shared<HeadlessDisplay>()), pixelRatio(pixelRatio_) {
- resize(width, height);
+ : display(std::make_shared<HeadlessDisplay>())
+ , pixelRatio(pixelRatio_)
+ , dimensions({{ width, height }})
+ , needsResize(true) {
}
HeadlessView::HeadlessView(std::shared_ptr<HeadlessDisplay> display_,
float pixelRatio_,
uint16_t width,
uint16_t height)
- : display(std::move(display_)), pixelRatio(pixelRatio_) {
- resize(width, height);
+ : display(std::move(display_))
+ , pixelRatio(pixelRatio_)
+ , dimensions({{ width, height }})
+ , needsResize(true) {
}
-void HeadlessView::loadExtensions() {
- if (extensionsLoaded) {
- return;
- }
-
-#ifdef MBGL_USE_CGL
- gl::InitializeExtensions([](const char * name) {
- static CFBundleRef framework = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.opengl"));
- if (!framework) {
- throw std::runtime_error("Failed to load OpenGL framework.");
- }
-
- CFStringRef str = CFStringCreateWithCString(kCFAllocatorDefault, name, kCFStringEncodingASCII);
- void* symbol = CFBundleGetFunctionPointerForName(framework, str);
- CFRelease(str);
-
- return reinterpret_cast<gl::glProc>(symbol);
- });
-#endif
-
-#ifdef MBGL_USE_GLX
- gl::InitializeExtensions([](const char * name) {
- return glXGetProcAddress(reinterpret_cast<const GLubyte *>(name));
- });
-#endif
-
- extensionsLoaded = true;
-}
-
-void HeadlessView::createContext() {
- if (!display) {
- throw std::runtime_error("Display is not set");
- }
-
-#if MBGL_USE_CGL
- CGLError error = CGLCreateContext(display->pixelFormat, NULL, &glContext);
- if (error != kCGLNoError) {
- throw std::runtime_error(std::string("Error creating GL context object:") + CGLErrorString(error) + "\n");
- }
-
- error = CGLEnable(glContext, kCGLCEMPEngine);
- if (error != kCGLNoError) {
- throw std::runtime_error(std::string("Error enabling OpenGL multithreading:") + CGLErrorString(error) + "\n");
- }
-#endif
-
-#if MBGL_USE_GLX
- xDisplay = display->xDisplay;
- fbConfigs = display->fbConfigs;
-
- if (!glContext) {
- // Try to create a legacy context
- glContext = glXCreateNewContext(xDisplay, fbConfigs[0], GLX_RGBA_TYPE, None, True);
- if (glContext) {
- if (!glXIsDirect(xDisplay, glContext)) {
- Log::Error(Event::OpenGL, "failed to create direct OpenGL Legacy context");
- glXDestroyContext(xDisplay, glContext);
- glContext = 0;
- }
- }
- }
-
- if (glContext == 0) {
- throw std::runtime_error("Error creating GL context object.");
- }
-
- // Create a dummy pbuffer. We will render to framebuffers anyway, but we need a pbuffer to
- // activate the context.
- int pbufferAttributes[] = {
- GLX_PBUFFER_WIDTH, 8,
- GLX_PBUFFER_HEIGHT, 8,
- None
- };
- glxPbuffer = glXCreatePbuffer(xDisplay, fbConfigs[0], pbufferAttributes);
-#endif
-}
-
-bool HeadlessView::isActive() const {
- return std::this_thread::get_id() == thread;
-}
-
-void HeadlessView::resizeFramebuffer() {
- assert(isActive());
-
- if (!needsResize) return;
-
+HeadlessView::~HeadlessView() {
+ activate();
clearBuffers();
+ deactivate();
- const unsigned int w = dimensions[0] * pixelRatio;
- const unsigned int h = dimensions[1] * pixelRatio;
-
- // Create depth/stencil buffer
- MBGL_CHECK_ERROR(glGenRenderbuffersEXT(1, &fboDepthStencil));
- MBGL_CHECK_ERROR(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, fboDepthStencil));
- MBGL_CHECK_ERROR(glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH24_STENCIL8_EXT, w, h));
- MBGL_CHECK_ERROR(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0));
-
- MBGL_CHECK_ERROR(glGenRenderbuffersEXT(1, &fboColor));
- MBGL_CHECK_ERROR(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, fboColor));
- MBGL_CHECK_ERROR(glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_RGBA8, w, h));
- MBGL_CHECK_ERROR(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0));
-
- MBGL_CHECK_ERROR(glGenFramebuffersEXT(1, &fbo));
- MBGL_CHECK_ERROR(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo));
-
- MBGL_CHECK_ERROR(glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, fboColor));
- MBGL_CHECK_ERROR(glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER_EXT, fboDepthStencil));
-
- GLenum status = MBGL_CHECK_ERROR(glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT));
-
- if (status != GL_FRAMEBUFFER_COMPLETE_EXT) {
- std::string error("Couldn't create framebuffer: ");
- switch (status) {
- case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT: (error += "incomplete attachment"); break;
- case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT: error += "incomplete missing attachment"; break;
- case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT: error += "incomplete dimensions"; break;
- case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT: error += "incomplete formats"; break;
- case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT: error += "incomplete draw buffer"; break;
- case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT: error += "incomplete read buffer"; break;
- case GL_FRAMEBUFFER_UNSUPPORTED: error += "unsupported"; break;
- default: error += "other"; break;
- }
- throw std::runtime_error(error);
- }
-
- MBGL_CHECK_ERROR(glViewport(0, 0, w, h));
-
- needsResize = false;
+ destroyContext();
}
void HeadlessView::resize(const uint16_t width, const uint16_t height) {
@@ -170,7 +41,7 @@ void HeadlessView::resize(const uint16_t width, const uint16_t height) {
}
PremultipliedImage HeadlessView::readStillImage() {
- assert(isActive());
+ assert(active);
const unsigned int w = dimensions[0] * pixelRatio;
const unsigned int h = dimensions[1] * pixelRatio;
@@ -178,7 +49,7 @@ PremultipliedImage HeadlessView::readStillImage() {
PremultipliedImage image { w, h };
MBGL_CHECK_ERROR(glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, image.data.get()));
- const int stride = image.stride();
+ const auto stride = image.stride();
auto tmp = std::make_unique<uint8_t[]>(stride);
uint8_t* rgba = image.data.get();
for (int i = 0, j = h - 1; i < j; i++, j--) {
@@ -190,50 +61,6 @@ PremultipliedImage HeadlessView::readStillImage() {
return image;
}
-void HeadlessView::clearBuffers() {
- assert(isActive());
-
- MBGL_CHECK_ERROR(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0));
-
- if (fbo) {
- MBGL_CHECK_ERROR(glDeleteFramebuffersEXT(1, &fbo));
- fbo = 0;
- }
-
- if (fboColor) {
- MBGL_CHECK_ERROR(glDeleteRenderbuffersEXT(1, &fboColor));
- fboColor = 0;
- }
-
- if (fboDepthStencil) {
- MBGL_CHECK_ERROR(glDeleteRenderbuffersEXT(1, &fboDepthStencil));
- fboDepthStencil = 0;
- }
-}
-
-HeadlessView::~HeadlessView() {
- activate();
- clearBuffers();
- deactivate();
-
-#if MBGL_USE_CGL
- CGLDestroyContext(glContext);
-#endif
-
-#if MBGL_USE_GLX
- if (glxPbuffer) {
- glXDestroyPbuffer(xDisplay, glxPbuffer);
- glxPbuffer = 0;
- }
-
- glXDestroyContext(xDisplay, glContext);
-#endif
-}
-
-void HeadlessView::notify() {
- // no-op
-}
-
float HeadlessView::getPixelRatio() const {
return pixelRatio;
}
@@ -248,61 +75,36 @@ std::array<uint16_t, 2> HeadlessView::getFramebufferSize() const {
}
void HeadlessView::activate() {
- if (thread != std::thread::id()) {
- throw std::runtime_error("OpenGL context was already current");
- }
- thread = std::this_thread::get_id();
+ active = true;
if (!glContext) {
+ if (!display) {
+ throw std::runtime_error("Display is not set");
+ }
createContext();
}
-#if MBGL_USE_CGL
- CGLError error = CGLSetCurrentContext(glContext);
- if (error != kCGLNoError) {
- throw std::runtime_error(std::string("Switching OpenGL context failed:") + CGLErrorString(error) + "\n");
- }
-#endif
+ activateContext();
-#if MBGL_USE_GLX
- if (!glXMakeContextCurrent(xDisplay, glxPbuffer, glxPbuffer, glContext)) {
- throw std::runtime_error("Switching OpenGL context failed.\n");
+ if (!extensionsLoaded) {
+ gl::InitializeExtensions(initializeExtension);
+ extensionsLoaded = true;
}
-#endif
- loadExtensions();
+ if (needsResize) {
+ clearBuffers();
+ resizeFramebuffer();
+ needsResize = false;
+ }
}
void HeadlessView::deactivate() {
- if (thread == std::thread::id()) {
- throw std::runtime_error("OpenGL context was not current");
- }
- thread = std::thread::id();
-
-#if MBGL_USE_CGL
- CGLError error = CGLSetCurrentContext(nullptr);
- if (error != kCGLNoError) {
- throw std::runtime_error(std::string("Removing OpenGL context failed:") + CGLErrorString(error) + "\n");
- }
-#endif
-
-#if MBGL_USE_GLX
- if (!glXMakeContextCurrent(xDisplay, 0, 0, nullptr)) {
- throw std::runtime_error("Removing OpenGL context failed.\n");
- }
-#endif
+ deactivateContext();
+ active = false;
}
void HeadlessView::invalidate() {
- // no-op
-}
-
-void HeadlessView::beforeRender() {
- resizeFramebuffer();
-}
-
-void HeadlessView::afterRender() {
- // no-op
+ assert(false);
}
} // namespace mbgl
diff --git a/platform/default/headless_view_glx.cpp b/platform/default/headless_view_glx.cpp
new file mode 100644
index 0000000000..3b719ab43a
--- /dev/null
+++ b/platform/default/headless_view_glx.cpp
@@ -0,0 +1,128 @@
+#include <mbgl/platform/default/headless_view.hpp>
+#include <mbgl/platform/default/headless_display.hpp>
+#include <mbgl/platform/log.hpp>
+
+#include <cassert>
+
+#include <GL/glx.h>
+
+namespace mbgl {
+
+gl::glProc HeadlessView::initializeExtension(const char* name) {
+ return glXGetProcAddress(reinterpret_cast<const GLubyte*>(name));
+}
+
+void HeadlessView::createContext() {
+ xDisplay = display->xDisplay;
+ fbConfigs = display->fbConfigs;
+
+ if (!glContext) {
+ // Try to create a legacy context
+ glContext = glXCreateNewContext(xDisplay, fbConfigs[0], GLX_RGBA_TYPE, None, True);
+ if (glContext) {
+ if (!glXIsDirect(xDisplay, glContext)) {
+ Log::Error(Event::OpenGL, "failed to create direct OpenGL Legacy context");
+ glXDestroyContext(xDisplay, glContext);
+ glContext = 0;
+ }
+ }
+ }
+
+ if (glContext == 0) {
+ throw std::runtime_error("Error creating GL context object.");
+ }
+
+ // Create a dummy pbuffer. We will render to framebuffers anyway, but we need a pbuffer to
+ // activate the context.
+ int pbufferAttributes[] = {
+ GLX_PBUFFER_WIDTH, 8,
+ GLX_PBUFFER_HEIGHT, 8,
+ None
+ };
+ glxPbuffer = glXCreatePbuffer(xDisplay, fbConfigs[0], pbufferAttributes);
+}
+
+void HeadlessView::destroyContext() {
+ if (glxPbuffer) {
+ glXDestroyPbuffer(xDisplay, glxPbuffer);
+ glxPbuffer = 0;
+ }
+
+ glXDestroyContext(xDisplay, glContext);
+}
+
+void HeadlessView::resizeFramebuffer() {
+ const unsigned int w = dimensions[0] * pixelRatio;
+ const unsigned int h = dimensions[1] * pixelRatio;
+
+ // Create depth/stencil buffer
+ MBGL_CHECK_ERROR(glGenRenderbuffersEXT(1, &fboDepthStencil));
+ MBGL_CHECK_ERROR(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, fboDepthStencil));
+ MBGL_CHECK_ERROR(glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH24_STENCIL8_EXT, w, h));
+ MBGL_CHECK_ERROR(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0));
+
+ MBGL_CHECK_ERROR(glGenRenderbuffersEXT(1, &fboColor));
+ MBGL_CHECK_ERROR(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, fboColor));
+ MBGL_CHECK_ERROR(glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_RGBA8, w, h));
+ MBGL_CHECK_ERROR(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0));
+
+ MBGL_CHECK_ERROR(glGenFramebuffersEXT(1, &fbo));
+ MBGL_CHECK_ERROR(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo));
+
+ MBGL_CHECK_ERROR(glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, fboColor));
+ MBGL_CHECK_ERROR(glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER_EXT, fboDepthStencil));
+
+ GLenum status = MBGL_CHECK_ERROR(glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT));
+
+ if (status != GL_FRAMEBUFFER_COMPLETE_EXT) {
+ std::string error("Couldn't create framebuffer: ");
+ switch (status) {
+ case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT: (error += "incomplete attachment"); break;
+ case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT: error += "incomplete missing attachment"; break;
+ case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT: error += "incomplete dimensions"; break;
+ case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT: error += "incomplete formats"; break;
+ case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT: error += "incomplete draw buffer"; break;
+ case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT: error += "incomplete read buffer"; break;
+ case GL_FRAMEBUFFER_UNSUPPORTED: error += "unsupported"; break;
+ default: error += "other"; break;
+ }
+ throw std::runtime_error(error);
+ }
+
+ MBGL_CHECK_ERROR(glViewport(0, 0, w, h));
+}
+
+void HeadlessView::clearBuffers() {
+ assert(active);
+
+ MBGL_CHECK_ERROR(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0));
+
+ if (fbo) {
+ MBGL_CHECK_ERROR(glDeleteFramebuffersEXT(1, &fbo));
+ fbo = 0;
+ }
+
+ if (fboColor) {
+ MBGL_CHECK_ERROR(glDeleteRenderbuffersEXT(1, &fboColor));
+ fboColor = 0;
+ }
+
+ if (fboDepthStencil) {
+ MBGL_CHECK_ERROR(glDeleteRenderbuffersEXT(1, &fboDepthStencil));
+ fboDepthStencil = 0;
+ }
+}
+
+void HeadlessView::activateContext() {
+ if (!glXMakeContextCurrent(xDisplay, glxPbuffer, glxPbuffer, glContext)) {
+ throw std::runtime_error("Switching OpenGL context failed.\n");
+ }
+}
+
+void HeadlessView::deactivateContext() {
+ if (!glXMakeContextCurrent(xDisplay, 0, 0, nullptr)) {
+ throw std::runtime_error("Removing OpenGL context failed.\n");
+ }
+}
+
+} // namespace mbgl
diff --git a/platform/default/http_request_curl.cpp b/platform/default/http_file_source.cpp
index 58c574fee1..e83ecfbfc9 100644
--- a/platform/default/http_request_curl.cpp
+++ b/platform/default/http_file_source.cpp
@@ -1,5 +1,4 @@
-#include <mbgl/storage/http_context_base.hpp>
-#include <mbgl/storage/http_request_base.hpp>
+#include <mbgl/storage/http_file_source.hpp>
#include <mbgl/storage/resource.hpp>
#include <mbgl/storage/response.hpp>
#include <mbgl/platform/log.hpp>
@@ -9,28 +8,23 @@
#include <mbgl/util/string.hpp>
#include <mbgl/util/timer.hpp>
#include <mbgl/util/chrono.hpp>
+#include <mbgl/util/http_header.hpp>
#include <curl/curl.h>
-#ifdef __ANDROID__
-#include <mbgl/android/jni.hpp>
-#include <zip.h>
-#include <openssl/ssl.h>
-#endif
-
#include <queue>
#include <map>
#include <cassert>
#include <cstring>
#include <cstdio>
-void handleError(CURLMcode code) {
+static void handleError(CURLMcode code) {
if (code != CURLM_OK) {
throw std::runtime_error(std::string("CURL multi error: ") + curl_multi_strerror(code));
}
}
-void handleError(CURLcode code) {
+static void handleError(CURLcode code) {
if (code != CURLE_OK) {
throw std::runtime_error(std::string("CURL easy error: ") + curl_easy_strerror(code));
}
@@ -38,20 +32,14 @@ void handleError(CURLcode code) {
namespace mbgl {
-class HTTPCURLRequest;
-
-class HTTPCURLContext : public HTTPContextBase {
- MBGL_STORE_THREAD(tid)
-
+class HTTPFileSource::Impl {
public:
- HTTPCURLContext();
- ~HTTPCURLContext();
-
- HTTPRequestBase* createRequest(const Resource&, HTTPRequestBase::Callback) final;
+ Impl();
+ ~Impl();
static int handleSocket(CURL *handle, curl_socket_t s, int action, void *userp, void *socketp);
static int startTimeout(CURLM *multi, long timeout_ms, void *userp);
- static void onTimeout(HTTPCURLContext *context);
+ static void onTimeout(HTTPFileSource::Impl *context);
void perform(curl_socket_t s, util::RunLoop::Event event);
CURL *getHandle();
@@ -73,14 +61,10 @@ public:
std::queue<CURL *> handles;
};
-class HTTPCURLRequest : public HTTPRequestBase {
- MBGL_STORE_THREAD(tid)
-
+class HTTPRequest : public AsyncRequest {
public:
- HTTPCURLRequest(HTTPCURLContext*, const Resource&, Callback);
- ~HTTPCURLRequest();
-
- void cancel() final;
+ HTTPRequest(HTTPFileSource::Impl*, const Resource&, FileSource::Callback);
+ ~HTTPRequest();
void handleResult(CURLcode code);
@@ -88,24 +72,21 @@ private:
static size_t headerCallback(char *const buffer, const size_t size, const size_t nmemb, void *userp);
static size_t writeCallback(void *const contents, const size_t size, const size_t nmemb, void *userp);
- HTTPCURLContext *context = nullptr;
+ HTTPFileSource::Impl* context = nullptr;
+ Resource resource;
+ FileSource::Callback callback;
// Will store the current response.
std::shared_ptr<std::string> data;
std::unique_ptr<Response> response;
- // In case of revalidation requests, this will store the old response.
- const std::shared_ptr<const Response> existingResponse;
-
CURL *handle = nullptr;
curl_slist *headers = nullptr;
- char error[CURL_ERROR_SIZE];
+ char error[CURL_ERROR_SIZE] = { 0 };
};
-// -------------------------------------------------------------------------------------------------
-
-HTTPCURLContext::HTTPCURLContext() {
+HTTPFileSource::Impl::Impl() {
if (curl_global_init(CURL_GLOBAL_ALL)) {
throw std::runtime_error("Could not init cURL");
}
@@ -119,7 +100,7 @@ HTTPCURLContext::HTTPCURLContext() {
handleError(curl_multi_setopt(multi, CURLMOPT_TIMERDATA, this));
}
-HTTPCURLContext::~HTTPCURLContext() {
+HTTPFileSource::Impl::~Impl() {
while (!handles.empty()) {
curl_easy_cleanup(handles.front());
handles.pop();
@@ -134,11 +115,7 @@ HTTPCURLContext::~HTTPCURLContext() {
timeout.stop();
}
-HTTPRequestBase* HTTPCURLContext::createRequest(const Resource& resource, HTTPRequestBase::Callback callback) {
- return new HTTPCURLRequest(this, resource, callback);
-}
-
-CURL *HTTPCURLContext::getHandle() {
+CURL *HTTPFileSource::Impl::getHandle() {
if (!handles.empty()) {
auto handle = handles.front();
handles.pop();
@@ -148,20 +125,19 @@ CURL *HTTPCURLContext::getHandle() {
}
}
-void HTTPCURLContext::returnHandle(CURL *handle) {
+void HTTPFileSource::Impl::returnHandle(CURL *handle) {
curl_easy_reset(handle);
handles.push(handle);
}
-void HTTPCURLContext::checkMultiInfo() {
- MBGL_VERIFY_THREAD(tid);
+void HTTPFileSource::Impl::checkMultiInfo() {
CURLMsg *message = nullptr;
int pending = 0;
while ((message = curl_multi_info_read(multi, &pending))) {
switch (message->msg) {
case CURLMSG_DONE: {
- HTTPCURLRequest *baton = nullptr;
+ HTTPRequest *baton = nullptr;
curl_easy_getinfo(message->easy_handle, CURLINFO_PRIVATE, (char *)&baton);
assert(baton);
baton->handleResult(message->data.result);
@@ -174,9 +150,7 @@ void HTTPCURLContext::checkMultiInfo() {
}
}
-void HTTPCURLContext::perform(curl_socket_t s, util::RunLoop::Event events) {
- MBGL_VERIFY_THREAD(tid);
-
+void HTTPFileSource::Impl::perform(curl_socket_t s, util::RunLoop::Event events) {
int flags = 0;
if (events == util::RunLoop::Event::Read) {
@@ -192,23 +166,22 @@ void HTTPCURLContext::perform(curl_socket_t s, util::RunLoop::Event events) {
checkMultiInfo();
}
-int HTTPCURLContext::handleSocket(CURL * /* handle */, curl_socket_t s, int action, void *userp,
+int HTTPFileSource::Impl::handleSocket(CURL * /* handle */, curl_socket_t s, int action, void *userp,
void * /* socketp */) {
assert(userp);
- auto context = reinterpret_cast<HTTPCURLContext *>(userp);
- MBGL_VERIFY_THREAD(context->tid);
+ auto context = reinterpret_cast<Impl *>(userp);
switch (action) {
case CURL_POLL_IN: {
using namespace std::placeholders;
util::RunLoop::Get()->addWatch(s, util::RunLoop::Event::Read,
- std::bind(&HTTPCURLContext::perform, context, _1, _2));
+ std::bind(&Impl::perform, context, _1, _2));
break;
}
case CURL_POLL_OUT: {
using namespace std::placeholders;
util::RunLoop::Get()->addWatch(s, util::RunLoop::Event::Write,
- std::bind(&HTTPCURLContext::perform, context, _1, _2));
+ std::bind(&Impl::perform, context, _1, _2));
break;
}
case CURL_POLL_REMOVE:
@@ -221,8 +194,7 @@ int HTTPCURLContext::handleSocket(CURL * /* handle */, curl_socket_t s, int acti
return 0;
}
-void HTTPCURLContext::onTimeout(HTTPCURLContext *context) {
- MBGL_VERIFY_THREAD(context->tid);
+void HTTPFileSource::Impl::onTimeout(Impl *context) {
int running_handles;
CURLMcode error = curl_multi_socket_action(context->multi, CURL_SOCKET_TIMEOUT, 0, &running_handles);
if (error != CURLM_OK) {
@@ -231,127 +203,27 @@ void HTTPCURLContext::onTimeout(HTTPCURLContext *context) {
context->checkMultiInfo();
}
-int HTTPCURLContext::startTimeout(CURLM * /* multi */, long timeout_ms, void *userp) {
+int HTTPFileSource::Impl::startTimeout(CURLM * /* multi */, long timeout_ms, void *userp) {
assert(userp);
- auto context = reinterpret_cast<HTTPCURLContext *>(userp);
- MBGL_VERIFY_THREAD(context->tid);
+ auto context = reinterpret_cast<Impl *>(userp);
+
if (timeout_ms < 0) {
// A timeout of 0 ms means that the timer will invoked in the next loop iteration.
timeout_ms = 0;
}
+
context->timeout.stop();
context->timeout.start(mbgl::Milliseconds(timeout_ms), Duration::zero(),
- std::bind(&HTTPCURLContext::onTimeout, context));
+ std::bind(&Impl::onTimeout, context));
return 0;
}
-// -------------------------------------------------------------------------------------------------
-
-#ifdef __ANDROID__
-
-// This function is called to load the CA bundle
-// from http://curl.haxx.se/libcurl/c/cacertinmem.html¯
-static CURLcode sslctx_function(CURL * /* curl */, void *sslctx, void * /* parm */) {
-
- int error = 0;
- struct zip *apk = zip_open(mbgl::android::apkPath.c_str(), 0, &error);
- if (apk == nullptr) {
- return CURLE_SSL_CACERT_BADFILE;
- }
-
- struct zip_file *apkFile = zip_fopen(apk, "assets/ca-bundle.crt", ZIP_FL_NOCASE);
- if (apkFile == nullptr) {
- zip_close(apk);
- apk = nullptr;
- return CURLE_SSL_CACERT_BADFILE;
- }
-
- struct zip_stat stat;
- if (zip_stat(apk, "assets/ca-bundle.crt", ZIP_FL_NOCASE, &stat) != 0) {
- zip_fclose(apkFile);
- apkFile = nullptr;
- zip_close(apk);
- apk = nullptr;
- return CURLE_SSL_CACERT_BADFILE;
- }
-
- if (stat.size > std::numeric_limits<int>::max()) {
- zip_fclose(apkFile);
- apkFile = nullptr;
- zip_close(apk);
- apk = nullptr;
- return CURLE_SSL_CACERT_BADFILE;
- }
-
- const auto pem = std::make_unique<char[]>(stat.size);
-
- if (static_cast<zip_uint64_t>(zip_fread(apkFile, reinterpret_cast<void *>(pem.get()), stat.size)) != stat.size) {
- zip_fclose(apkFile);
- apkFile = nullptr;
- zip_close(apk);
- apk = nullptr;
- return CURLE_SSL_CACERT_BADFILE;
- }
-
- // get a pointer to the X509 certificate store (which may be empty!)
- X509_STORE *store = SSL_CTX_get_cert_store((SSL_CTX *)sslctx);
- if (store == nullptr) {
- return CURLE_SSL_CACERT_BADFILE;
- }
-
- // get a BIO
- BIO *bio = BIO_new_mem_buf(pem.get(), static_cast<int>(stat.size));
- if (bio == nullptr) {
- store = nullptr;
- return CURLE_SSL_CACERT_BADFILE;
- }
-
- // use it to read the PEM formatted certificate from memory into an X509
- // structure that SSL can use
- X509 *cert = nullptr;
- while (PEM_read_bio_X509(bio, &cert, 0, nullptr) != nullptr) {
- if (cert == nullptr) {
- BIO_free(bio);
- bio = nullptr;
- store = nullptr;
- return CURLE_SSL_CACERT_BADFILE;
- }
-
- // add our certificate to this store
- if (X509_STORE_add_cert(store, cert) == 0) {
- X509_free(cert);
- cert = nullptr;
- BIO_free(bio);
- bio = nullptr;
- store = nullptr;
- return CURLE_SSL_CACERT_BADFILE;
- }
-
- X509_free(cert);
- cert = nullptr;
- }
-
- // decrease reference counts
- BIO_free(bio);
- bio = nullptr;
-
- zip_fclose(apkFile);
- apkFile = nullptr;
- zip_close(apk);
- apk = nullptr;
-
- // all set to go
- return CURLE_OK;
-}
-#endif
-
-HTTPCURLRequest::HTTPCURLRequest(HTTPCURLContext* context_, const Resource& resource_, Callback callback_)
- : HTTPRequestBase(resource_, callback_),
- context(context_),
+HTTPRequest::HTTPRequest(HTTPFileSource::Impl* context_, const Resource& resource_, FileSource::Callback callback_)
+ : context(context_),
+ resource(resource_),
+ callback(callback_),
handle(context->getHandle()) {
- // Zero out the error buffer.
- memset(error, 0, sizeof(error));
// If there's already a response, set the correct etags/modified headers to make sure we are
// getting a 304 response if possible. This avoids redownloading unchanged data.
@@ -370,12 +242,7 @@ HTTPCURLRequest::HTTPCURLRequest(HTTPCURLContext* context_, const Resource& reso
handleError(curl_easy_setopt(handle, CURLOPT_PRIVATE, this));
handleError(curl_easy_setopt(handle, CURLOPT_ERRORBUFFER, error));
-#ifdef __ANDROID__
- handleError(curl_easy_setopt(handle, CURLOPT_SSLCERTTYPE, "PEM"));
- handleError(curl_easy_setopt(handle, CURLOPT_SSL_CTX_FUNCTION, sslctx_function));
-#else
handleError(curl_easy_setopt(handle, CURLOPT_CAINFO, "ca-bundle.crt"));
-#endif
handleError(curl_easy_setopt(handle, CURLOPT_FOLLOWLOCATION, 1));
handleError(curl_easy_setopt(handle, CURLOPT_URL, resource.url.c_str()));
handleError(curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, writeCallback));
@@ -394,9 +261,7 @@ HTTPCURLRequest::HTTPCURLRequest(HTTPCURLContext* context_, const Resource& reso
handleError(curl_multi_add_handle(context->multi, handle));
}
-HTTPCURLRequest::~HTTPCURLRequest() {
- MBGL_VERIFY_THREAD(tid);
-
+HTTPRequest::~HTTPRequest() {
handleError(curl_multi_remove_handle(context->multi, handle));
context->returnHandle(handle);
handle = nullptr;
@@ -407,16 +272,11 @@ HTTPCURLRequest::~HTTPCURLRequest() {
}
}
-void HTTPCURLRequest::cancel() {
- delete this;
-}
-
// This function is called when we have new data for a request. We just append it to the string
// containing the previous data.
-size_t HTTPCURLRequest::writeCallback(void *const contents, const size_t size, const size_t nmemb, void *userp) {
+size_t HTTPRequest::writeCallback(void *const contents, const size_t size, const size_t nmemb, void *userp) {
assert(userp);
- auto impl = reinterpret_cast<HTTPCURLRequest *>(userp);
- MBGL_VERIFY_THREAD(impl->tid);
+ auto impl = reinterpret_cast<HTTPRequest *>(userp);
if (!impl->data) {
impl->data = std::make_shared<std::string>();
@@ -442,10 +302,9 @@ size_t headerMatches(const char *const header, const char *const buffer, const s
return i == headerLength ? i : std::string::npos;
}
-size_t HTTPCURLRequest::headerCallback(char *const buffer, const size_t size, const size_t nmemb, void *userp) {
+size_t HTTPRequest::headerCallback(char *const buffer, const size_t size, const size_t nmemb, void *userp) {
assert(userp);
- auto baton = reinterpret_cast<HTTPCURLRequest *>(userp);
- MBGL_VERIFY_THREAD(baton->tid);
+ auto baton = reinterpret_cast<HTTPRequest *>(userp);
if (!baton->response) {
baton->response = std::make_unique<Response>();
@@ -457,30 +316,21 @@ size_t HTTPCURLRequest::headerCallback(char *const buffer, const size_t size, co
// Always overwrite the modification date; We might already have a value here from the
// Date header, but this one is more accurate.
const std::string value { buffer + begin, length - begin - 2 }; // remove \r\n
- baton->response->modified = SystemClock::from_time_t(curl_getdate(value.c_str(), nullptr));
+ baton->response->modified = Timestamp{ Seconds(curl_getdate(value.c_str(), nullptr)) };
} else if ((begin = headerMatches("etag: ", buffer, length)) != std::string::npos) {
baton->response->etag = std::string(buffer + begin, length - begin - 2); // remove \r\n
} else if ((begin = headerMatches("cache-control: ", buffer, length)) != std::string::npos) {
const std::string value { buffer + begin, length - begin - 2 }; // remove \r\n
- baton->response->expires = parseCacheControl(value.c_str());
+ baton->response->expires = http::CacheControl::parse(value.c_str()).toTimePoint();
} else if ((begin = headerMatches("expires: ", buffer, length)) != std::string::npos) {
const std::string value { buffer + begin, length - begin - 2 }; // remove \r\n
- baton->response->expires = SystemClock::from_time_t(curl_getdate(value.c_str(), nullptr));
+ baton->response->expires = Timestamp{ Seconds(curl_getdate(value.c_str(), nullptr)) };
}
return length;
}
-void HTTPCURLRequest::handleResult(CURLcode code) {
- MBGL_VERIFY_THREAD(tid);
-
- if (cancelled) {
- // In this case, it doesn't make sense to even process the response even further since
- // the request was canceled anyway.
- delete this;
- return;
- }
-
+void HTTPRequest::handleResult(CURLcode code) {
// Make sure a response object exists in case we haven't got any headers or content.
if (!response) {
response = std::make_unique<Response>();
@@ -525,24 +375,31 @@ void HTTPCURLRequest::handleResult(CURLcode code) {
} else if (responseCode >= 500 && responseCode < 600) {
response->error =
std::make_unique<Error>(Error::Reason::Server, std::string{ "HTTP status code " } +
- std::to_string(responseCode));
+ util::toString(responseCode));
} else {
response->error =
std::make_unique<Error>(Error::Reason::Other, std::string{ "HTTP status code " } +
- std::to_string(responseCode));
+ util::toString(responseCode));
}
}
- // Actually return the response.
- notify(*response);
- delete this;
+ // Calling `callback` may result in deleting `this`. Copy data to temporaries first.
+ auto callback_ = callback;
+ auto response_ = *response;
+ callback_(response_);
}
-std::unique_ptr<HTTPContextBase> HTTPContextBase::createContext() {
- return std::make_unique<HTTPCURLContext>();
+HTTPFileSource::HTTPFileSource()
+ : impl(std::make_unique<Impl>()) {
+}
+
+HTTPFileSource::~HTTPFileSource() = default;
+
+std::unique_ptr<AsyncRequest> HTTPFileSource::request(const Resource& resource, Callback callback) {
+ return std::make_unique<HTTPRequest>(impl.get(), resource, callback);
}
-uint32_t HTTPContextBase::maximumConcurrentRequests() {
+uint32_t HTTPFileSource::maximumConcurrentRequests() {
return 20;
}
diff --git a/platform/default/image.cpp b/platform/default/image.cpp
index 71fb5414b3..988d2f4a4e 100644
--- a/platform/default/image.cpp
+++ b/platform/default/image.cpp
@@ -4,20 +4,24 @@
#include <png.h>
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wunused-variable"
-// Check png library version.
-const static bool png_version_check = []() {
+template<size_t max, typename... Args>
+inline static std::string sprintf(const char *msg, Args... args) {
+ char res[max];
+ int len = snprintf(res, sizeof(res), msg, args...);
+ return std::string(res, len);
+}
+
+const static bool png_version_check __attribute__((unused)) = []() {
const png_uint_32 version = png_access_version_number();
if (version != PNG_LIBPNG_VER) {
- throw std::runtime_error(mbgl::util::sprintf<96>(
+ throw std::runtime_error(sprintf<96>(
"libpng version mismatch: headers report %d.%d.%d, but library reports %d.%d.%d",
PNG_LIBPNG_VER / 10000, (PNG_LIBPNG_VER / 100) % 100, PNG_LIBPNG_VER % 100,
version / 10000, (version / 100) % 100, version % 100));
}
return true;
}();
-#pragma GCC diagnostic pop
+
namespace mbgl {
std::string encodePNG(const PremultipliedImage& pre) {
diff --git a/platform/default/mbgl/storage/offline.cpp b/platform/default/mbgl/storage/offline.cpp
index 931e079771..d8e0357ae2 100644
--- a/platform/default/mbgl/storage/offline.cpp
+++ b/platform/default/mbgl/storage/offline.cpp
@@ -1,6 +1,6 @@
#include <mbgl/storage/offline.hpp>
#include <mbgl/util/tile_cover.hpp>
-#include <mbgl/source/source_info.hpp>
+#include <mbgl/util/tileset.hpp>
#include <rapidjson/document.h>
#include <rapidjson/stringbuffer.h>
@@ -23,20 +23,20 @@ OfflineTilePyramidRegionDefinition::OfflineTilePyramidRegionDefinition(
}
}
-std::vector<TileID> OfflineTilePyramidRegionDefinition::tileCover(SourceType type, uint16_t tileSize, const SourceInfo& info) const {
- double minZ = std::max<double>(coveringZoomLevel(minZoom, type, tileSize), info.minZoom);
- double maxZ = std::min<double>(coveringZoomLevel(maxZoom, type, tileSize), info.maxZoom);
+std::vector<CanonicalTileID> OfflineTilePyramidRegionDefinition::tileCover(SourceType type, uint16_t tileSize, const Tileset& tileset) const {
+ double minZ = std::max<double>(util::coveringZoomLevel(minZoom, type, tileSize), tileset.minZoom);
+ double maxZ = std::min<double>(util::coveringZoomLevel(maxZoom, type, tileSize), tileset.maxZoom);
assert(minZ >= 0);
assert(maxZ >= 0);
assert(minZ < std::numeric_limits<uint8_t>::max());
assert(maxZ < std::numeric_limits<uint8_t>::max());
- std::vector<TileID> result;
+ std::vector<CanonicalTileID> result;
for (uint8_t z = minZ; z <= maxZ; z++) {
- for (const auto& tile : mbgl::tileCover(bounds, z, z)) {
- result.push_back(tile.normalized());
+ for (const auto& tile : util::tileCover(bounds, z)) {
+ result.emplace_back(tile.canonical);
}
}
diff --git a/platform/default/mbgl/storage/offline_database.cpp b/platform/default/mbgl/storage/offline_database.cpp
index 0b8dec01bf..3193909294 100644
--- a/platform/default/mbgl/storage/offline_database.cpp
+++ b/platform/default/mbgl/storage/offline_database.cpp
@@ -4,7 +4,6 @@
#include <mbgl/util/io.hpp>
#include <mbgl/util/string.hpp>
#include <mbgl/util/chrono.hpp>
-#include <mbgl/map/tile_id.hpp>
#include <mbgl/platform/log.hpp>
#include "sqlite3.hpp"
@@ -158,7 +157,7 @@ std::pair<bool, uint64_t> OfflineDatabase::putInternal(const Resource& resource,
}
if (evict_ && !evict(size)) {
- Log::Warning(Event::Database, "Unable to make space for entry");
+ Log::Debug(Event::Database, "Unable to make space for entry");
return { false, 0 };
}
@@ -182,7 +181,7 @@ optional<std::pair<Response, uint64_t>> OfflineDatabase::getResource(const Resou
Statement accessedStmt = getStatement(
"UPDATE resources SET accessed = ?1 WHERE url = ?2");
- accessedStmt->bind(1, SystemClock::now());
+ accessedStmt->bind(1, util::now());
accessedStmt->bind(2, resource.url);
accessedStmt->run();
@@ -202,8 +201,8 @@ optional<std::pair<Response, uint64_t>> OfflineDatabase::getResource(const Resou
uint64_t size = 0;
response.etag = stmt->get<optional<std::string>>(0);
- response.expires = stmt->get<optional<SystemTimePoint>>(1);
- response.modified = stmt->get<optional<SystemTimePoint>>(2);
+ response.expires = stmt->get<optional<Timestamp>>(1);
+ response.modified = stmt->get<optional<Timestamp>>(2);
optional<std::string> data = stmt->get<optional<std::string>>(3);
if (!data) {
@@ -230,7 +229,7 @@ bool OfflineDatabase::putResource(const Resource& resource,
" expires = ?2 "
"WHERE url = ?3 ");
- update->bind(1, SystemClock::now());
+ update->bind(1, util::now());
update->bind(2, response.expires);
update->bind(3, resource.url);
update->run();
@@ -239,6 +238,10 @@ bool OfflineDatabase::putResource(const Resource& resource,
// We can't use REPLACE because it would change the id value.
+ // Begin an immediate-mode transaction to ensure that two writers do not attempt
+ // to INSERT a resource at the same moment.
+ Transaction transaction(*db, Transaction::Immediate);
+
Statement update = getStatement(
"UPDATE resources "
"SET kind = ?1, "
@@ -254,7 +257,7 @@ bool OfflineDatabase::putResource(const Resource& resource,
update->bind(2, response.etag);
update->bind(3, response.expires);
update->bind(4, response.modified);
- update->bind(5, SystemClock::now());
+ update->bind(5, util::now());
update->bind(8, resource.url);
if (response.noContent) {
@@ -267,6 +270,7 @@ bool OfflineDatabase::putResource(const Resource& resource,
update->run();
if (db->changes() != 0) {
+ transaction.commit();
return false;
}
@@ -279,7 +283,7 @@ bool OfflineDatabase::putResource(const Resource& resource,
insert->bind(3, response.etag);
insert->bind(4, response.expires);
insert->bind(5, response.modified);
- insert->bind(6, SystemClock::now());
+ insert->bind(6, util::now());
if (response.noContent) {
insert->bind(7, nullptr);
@@ -290,6 +294,8 @@ bool OfflineDatabase::putResource(const Resource& resource,
}
insert->run();
+ transaction.commit();
+
return true;
}
@@ -303,7 +309,7 @@ optional<std::pair<Response, uint64_t>> OfflineDatabase::getTile(const Resource:
" AND y = ?5 "
" AND z = ?6 ");
- accessedStmt->bind(1, SystemClock::now());
+ accessedStmt->bind(1, util::now());
accessedStmt->bind(2, tile.urlTemplate);
accessedStmt->bind(3, tile.pixelRatio);
accessedStmt->bind(4, tile.x);
@@ -335,8 +341,8 @@ optional<std::pair<Response, uint64_t>> OfflineDatabase::getTile(const Resource:
uint64_t size = 0;
response.etag = stmt->get<optional<std::string>>(0);
- response.expires = stmt->get<optional<SystemTimePoint>>(1);
- response.modified = stmt->get<optional<SystemTimePoint>>(2);
+ response.expires = stmt->get<optional<Timestamp>>(1);
+ response.modified = stmt->get<optional<Timestamp>>(2);
optional<std::string> data = stmt->get<optional<std::string>>(3);
if (!data) {
@@ -367,7 +373,7 @@ bool OfflineDatabase::putTile(const Resource::TileData& tile,
" AND y = ?6 "
" AND z = ?7 ");
- update->bind(1, SystemClock::now());
+ update->bind(1, util::now());
update->bind(2, response.expires);
update->bind(3, tile.urlTemplate);
update->bind(4, tile.pixelRatio);
@@ -380,6 +386,10 @@ bool OfflineDatabase::putTile(const Resource::TileData& tile,
// We can't use REPLACE because it would change the id value.
+ // Begin an immediate-mode transaction to ensure that two writers do not attempt
+ // to INSERT a resource at the same moment.
+ Transaction transaction(*db, Transaction::Immediate);
+
Statement update = getStatement(
"UPDATE tiles "
"SET modified = ?1, "
@@ -397,7 +407,7 @@ bool OfflineDatabase::putTile(const Resource::TileData& tile,
update->bind(1, response.modified);
update->bind(2, response.etag);
update->bind(3, response.expires);
- update->bind(4, SystemClock::now());
+ update->bind(4, util::now());
update->bind(7, tile.urlTemplate);
update->bind(8, tile.pixelRatio);
update->bind(9, tile.x);
@@ -414,6 +424,7 @@ bool OfflineDatabase::putTile(const Resource::TileData& tile,
update->run();
if (db->changes() != 0) {
+ transaction.commit();
return false;
}
@@ -429,7 +440,7 @@ bool OfflineDatabase::putTile(const Resource::TileData& tile,
insert->bind(6, response.modified);
insert->bind(7, response.etag);
insert->bind(8, response.expires);
- insert->bind(9, SystemClock::now());
+ insert->bind(9, util::now());
if (response.noContent) {
insert->bind(10, nullptr);
@@ -440,6 +451,8 @@ bool OfflineDatabase::putTile(const Resource::TileData& tile,
}
insert->run();
+ transaction.commit();
+
return true;
}
@@ -594,26 +607,37 @@ OfflineRegionDefinition OfflineDatabase::getRegionDefinition(int64_t regionID) {
OfflineRegionStatus OfflineDatabase::getRegionCompletedStatus(int64_t regionID) {
OfflineRegionStatus result;
- Statement stmt = getStatement(
- "SELECT COUNT(*), SUM(size) FROM ( "
- " SELECT LENGTH(data) as size "
- " FROM region_resources, resources "
- " WHERE region_id = ?1 "
- " AND resource_id = resources.id "
- " UNION ALL "
- " SELECT LENGTH(data) as size "
- " FROM region_tiles, tiles "
- " WHERE region_id = ?1 "
- " AND tile_id = tiles.id "
- ") ");
+ std::tie(result.completedResourceCount, result.completedResourceSize)
+ = getCompletedResourceCountAndSize(regionID);
+ std::tie(result.completedTileCount, result.completedTileSize)
+ = getCompletedTileCountAndSize(regionID);
+
+ result.completedResourceCount += result.completedTileCount;
+ result.completedResourceSize += result.completedTileSize;
+
+ return result;
+}
+std::pair<int64_t, int64_t> OfflineDatabase::getCompletedResourceCountAndSize(int64_t regionID) {
+ Statement stmt = getStatement(
+ "SELECT COUNT(*), SUM(LENGTH(data)) "
+ "FROM region_resources, resources "
+ "WHERE region_id = ?1 "
+ "AND resource_id = resources.id ");
stmt->bind(1, regionID);
stmt->run();
+ return { stmt->get<int64_t>(0), stmt->get<int64_t>(1) };
+}
- result.completedResourceCount = stmt->get<int64_t>(0);
- result.completedResourceSize = stmt->get<int64_t>(1);
-
- return result;
+std::pair<int64_t, int64_t> OfflineDatabase::getCompletedTileCountAndSize(int64_t regionID) {
+ Statement stmt = getStatement(
+ "SELECT COUNT(*), SUM(LENGTH(data)) "
+ "FROM region_tiles, tiles "
+ "WHERE region_id = ?1 "
+ "AND tile_id = tiles.id ");
+ stmt->bind(1, regionID);
+ stmt->run();
+ return { stmt->get<int64_t>(0), stmt->get<int64_t>(1) };
}
template <class T>
diff --git a/platform/default/mbgl/storage/offline_database.hpp b/platform/default/mbgl/storage/offline_database.hpp
index 1e77d560d4..1706c6ba5a 100644
--- a/platform/default/mbgl/storage/offline_database.hpp
+++ b/platform/default/mbgl/storage/offline_database.hpp
@@ -1,5 +1,4 @@
-#ifndef MBGL_OFFLINE_DATABASE
-#define MBGL_OFFLINE_DATABASE
+#pragma once
#include <mbgl/storage/resource.hpp>
#include <mbgl/storage/offline.hpp>
@@ -92,6 +91,9 @@ private:
// Return value is true iff the resource was previously unused by any other regions.
bool markUsed(int64_t regionID, const Resource&);
+ std::pair<int64_t, int64_t> getCompletedResourceCountAndSize(int64_t regionID);
+ std::pair<int64_t, int64_t> getCompletedTileCountAndSize(int64_t regionID);
+
const std::string path;
std::unique_ptr<::mapbox::sqlite::Database> db;
std::unordered_map<const char *, std::unique_ptr<::mapbox::sqlite::Statement>> statements;
@@ -108,5 +110,3 @@ private:
};
} // namespace mbgl
-
-#endif
diff --git a/platform/default/mbgl/storage/offline_download.cpp b/platform/default/mbgl/storage/offline_download.cpp
index cda00bf8df..11ca862925 100644
--- a/platform/default/mbgl/storage/offline_download.cpp
+++ b/platform/default/mbgl/storage/offline_download.cpp
@@ -3,8 +3,7 @@
#include <mbgl/storage/file_source.hpp>
#include <mbgl/storage/resource.hpp>
#include <mbgl/storage/response.hpp>
-#include <mbgl/style/style_parser.hpp>
-#include <mbgl/layer/symbol_layer.hpp>
+#include <mbgl/style/parser.hpp>
#include <mbgl/text/glyph.hpp>
#include <mbgl/util/tile_cover.hpp>
#include <mbgl/util/mapbox.hpp>
@@ -47,7 +46,7 @@ void OfflineDownload::setState(OfflineRegionDownloadState state) {
observer->statusChanged(status);
}
-std::vector<Resource> OfflineDownload::spriteResources(const StyleParser& parser) const {
+std::vector<Resource> OfflineDownload::spriteResources(const style::Parser& parser) const {
std::vector<Resource> result;
if (!parser.spriteURL.empty()) {
@@ -58,7 +57,7 @@ std::vector<Resource> OfflineDownload::spriteResources(const StyleParser& parser
return result;
}
-std::vector<Resource> OfflineDownload::glyphResources(const StyleParser& parser) const {
+std::vector<Resource> OfflineDownload::glyphResources(const style::Parser& parser) const {
std::vector<Resource> result;
if (!parser.glyphURL.empty()) {
@@ -72,11 +71,11 @@ std::vector<Resource> OfflineDownload::glyphResources(const StyleParser& parser)
return result;
}
-std::vector<Resource> OfflineDownload::tileResources(SourceType type, uint16_t tileSize, const SourceInfo& info) const {
+std::vector<Resource> OfflineDownload::tileResources(SourceType type, uint16_t tileSize, const Tileset& tileset) const {
std::vector<Resource> result;
- for (const auto& tile : definition.tileCover(type, tileSize, info)) {
- result.push_back(Resource::tile(info.tiles[0], definition.pixelRatio, tile.x, tile.y, tile.z));
+ for (const auto& tile : definition.tileCover(type, tileSize, tileset)) {
+ result.push_back(Resource::tile(tileset.tiles[0], definition.pixelRatio, tile.x, tile.y, tile.z));
}
return result;
@@ -95,7 +94,7 @@ OfflineRegionStatus OfflineDownload::getStatus() const {
return result;
}
- StyleParser parser;
+ style::Parser parser;
parser.parse(*styleResponse->data);
result.requiredResourceCountIsPrecise = true;
@@ -104,14 +103,14 @@ OfflineRegionStatus OfflineDownload::getStatus() const {
switch (source->type) {
case SourceType::Vector:
case SourceType::Raster:
- if (source->getInfo()) {
- result.requiredResourceCount += tileResources(source->type, source->tileSize, *source->getInfo()).size();
+ if (source->getTileset()) {
+ result.requiredResourceCount += tileResources(source->type, source->tileSize, *source->getTileset()).size();
} else {
result.requiredResourceCount += 1;
optional<Response> sourceResponse = offlineDatabase.get(Resource::source(source->url));
if (sourceResponse) {
result.requiredResourceCount += tileResources(source->type, source->tileSize,
- *StyleParser::parseTileJSON(*sourceResponse->data, source->url, source->type, source->tileSize)).size();
+ *style::parseTileJSON(*sourceResponse->data, source->url, source->type, source->tileSize)).size();
} else {
result.requiredResourceCountIsPrecise = false;
}
@@ -145,7 +144,7 @@ void OfflineDownload::activateDownload() {
ensureResource(Resource::style(definition.styleURL), [&] (Response styleResponse) {
status.requiredResourceCountIsPrecise = true;
- StyleParser parser;
+ style::Parser parser;
parser.parse(*styleResponse.data);
for (const auto& source : parser.sources) {
@@ -156,14 +155,14 @@ void OfflineDownload::activateDownload() {
switch (type) {
case SourceType::Vector:
case SourceType::Raster:
- if (source->getInfo()) {
- ensureTiles(type, tileSize, *source->getInfo());
+ if (source->getTileset()) {
+ ensureTiles(type, tileSize, *source->getTileset());
} else {
status.requiredResourceCountIsPrecise = false;
requiredSourceURLs.insert(url);
ensureResource(Resource::source(url), [=] (Response sourceResponse) {
- ensureTiles(type, tileSize, *StyleParser::parseTileJSON(*sourceResponse.data, url, type, tileSize));
+ ensureTiles(type, tileSize, *style::parseTileJSON(*sourceResponse.data, url, type, tileSize));
requiredSourceURLs.erase(url);
if (requiredSourceURLs.empty()) {
@@ -196,11 +195,10 @@ void OfflineDownload::activateDownload() {
}
void OfflineDownload::deactivateDownload() {
- workRequests.clear();
- fileRequests.clear();
+ requests.clear();
}
-void OfflineDownload::ensureTiles(SourceType type, uint16_t tileSize, const SourceInfo& info) {
+void OfflineDownload::ensureTiles(SourceType type, uint16_t tileSize, const Tileset& info) {
for (const auto& resource : tileResources(type, tileSize, info)) {
ensureResource(resource);
}
@@ -209,9 +207,9 @@ void OfflineDownload::ensureTiles(SourceType type, uint16_t tileSize, const Sour
void OfflineDownload::ensureResource(const Resource& resource, std::function<void (Response)> callback) {
status.requiredResourceCount++;
- auto workRequestsIt = workRequests.insert(workRequests.begin(), nullptr);
+ auto workRequestsIt = requests.insert(requests.begin(), nullptr);
*workRequestsIt = util::RunLoop::Get()->invokeCancellable([=] () {
- workRequests.erase(workRequestsIt);
+ requests.erase(workRequestsIt);
optional<std::pair<Response, uint64_t>> offlineResponse = offlineDatabase.getRegionResource(id, resource);
if (offlineResponse) {
@@ -221,6 +219,10 @@ void OfflineDownload::ensureResource(const Resource& resource, std::function<voi
status.completedResourceCount++;
status.completedResourceSize += offlineResponse->second;
+ if (resource.kind == Resource::Kind::Tile) {
+ status.completedTileSize += offlineResponse->second;
+ }
+
observer->statusChanged(status);
if (status.complete()) {
@@ -234,21 +236,25 @@ void OfflineDownload::ensureResource(const Resource& resource, std::function<voi
return;
}
- auto fileRequestsIt = fileRequests.insert(fileRequests.begin(), nullptr);
+ auto fileRequestsIt = requests.insert(requests.begin(), nullptr);
*fileRequestsIt = onlineFileSource.request(resource, [=] (Response onlineResponse) {
if (onlineResponse.error) {
observer->responseError(*onlineResponse.error);
return;
}
- fileRequests.erase(fileRequestsIt);
+ requests.erase(fileRequestsIt);
if (callback) {
callback(onlineResponse);
}
status.completedResourceCount++;
- status.completedResourceSize += offlineDatabase.putRegionResource(id, resource, onlineResponse);
+ uint64_t resourceSize = offlineDatabase.putRegionResource(id, resource, onlineResponse);
+ status.completedResourceSize += resourceSize;
+ if (resource.kind == Resource::Kind::Tile) {
+ status.completedTileSize += resourceSize;
+ }
observer->statusChanged(status);
diff --git a/platform/default/mbgl/storage/offline_download.hpp b/platform/default/mbgl/storage/offline_download.hpp
index be8e90b251..1a0d7536d8 100644
--- a/platform/default/mbgl/storage/offline_download.hpp
+++ b/platform/default/mbgl/storage/offline_download.hpp
@@ -1,7 +1,6 @@
- #pragma once
+#pragma once
#include <mbgl/storage/offline.hpp>
-#include <mbgl/style/types.hpp>
#include <list>
#include <set>
@@ -11,13 +10,14 @@ namespace mbgl {
class OfflineDatabase;
class FileSource;
-class WorkRequest;
-class FileRequest;
+class AsyncRequest;
class Resource;
class Response;
-class SourceInfo;
-class StyleParser;
-class Source;
+class Tileset;
+
+namespace style {
+class Parser;
+}
/**
* Coordinates the request and storage of all resources for an offline region.
@@ -38,9 +38,9 @@ private:
void activateDownload();
void deactivateDownload();
- std::vector<Resource> spriteResources(const StyleParser&) const;
- std::vector<Resource> glyphResources(const StyleParser&) const;
- std::vector<Resource> tileResources(SourceType, uint16_t, const SourceInfo&) const;
+ std::vector<Resource> spriteResources(const style::Parser&) const;
+ std::vector<Resource> glyphResources(const style::Parser&) const;
+ std::vector<Resource> tileResources(SourceType, uint16_t, const Tileset&) const;
/*
* Ensure that the resource is stored in the database, requesting it if necessary.
@@ -48,7 +48,7 @@ private:
* is deactivated, all in progress requests are cancelled.
*/
void ensureResource(const Resource&, std::function<void (Response)> = {});
- void ensureTiles(SourceType, uint16_t, const SourceInfo&);
+ void ensureTiles(SourceType, uint16_t, const Tileset&);
bool checkTileCountLimit(const Resource& resource);
int64_t id;
@@ -57,8 +57,7 @@ private:
FileSource& onlineFileSource;
OfflineRegionStatus status;
std::unique_ptr<OfflineRegionObserver> observer;
- std::list<std::unique_ptr<WorkRequest>> workRequests;
- std::list<std::unique_ptr<FileRequest>> fileRequests;
+ std::list<std::unique_ptr<AsyncRequest>> requests;
std::set<std::string> requiredSourceURLs;
};
diff --git a/platform/default/online_file_source.cpp b/platform/default/online_file_source.cpp
index 944bcd56d5..a4ac2c2b2b 100644
--- a/platform/default/online_file_source.cpp
+++ b/platform/default/online_file_source.cpp
@@ -1,5 +1,5 @@
#include <mbgl/storage/online_file_source.hpp>
-#include <mbgl/storage/http_context_base.hpp>
+#include <mbgl/storage/http_file_source.hpp>
#include <mbgl/storage/network_status.hpp>
#include <mbgl/storage/response.hpp>
@@ -22,20 +22,20 @@
namespace mbgl {
-class OnlineFileRequestImpl : public util::noncopyable {
+class OnlineFileRequest : public AsyncRequest {
public:
using Callback = std::function<void (Response)>;
- OnlineFileRequestImpl(FileRequest*, const Resource&, Callback, OnlineFileSource::Impl&);
- ~OnlineFileRequestImpl();
+ OnlineFileRequest(const Resource&, Callback, OnlineFileSource::Impl&);
+ ~OnlineFileRequest();
- void networkIsReachableAgain(OnlineFileSource::Impl&);
- void schedule(OnlineFileSource::Impl&, optional<SystemTimePoint> expires);
- void completed(OnlineFileSource::Impl&, Response);
+ void networkIsReachableAgain();
+ void schedule(optional<Timestamp> expires);
+ void completed(Response);
- FileRequest* key;
+ OnlineFileSource::Impl& impl;
Resource resource;
- HTTPRequestBase* request = nullptr;
+ std::unique_ptr<AsyncRequest> request;
util::Timer timer;
Callback callback;
@@ -52,8 +52,7 @@ public:
class OnlineFileSource::Impl {
public:
- // Dummy parameter is a workaround for a gcc 4.9 bug.
- Impl(int) {
+ Impl() {
NetworkStatus::Subscribe(&reachability);
}
@@ -61,16 +60,16 @@ public:
NetworkStatus::Unsubscribe(&reachability);
}
- void request(FileRequest* key, Resource resource, Callback callback) {
- allRequests[key] = std::make_unique<OnlineFileRequestImpl>(key, resource, callback, *this);
+ void add(OnlineFileRequest* request) {
+ allRequests.insert(request);
}
- void cancel(FileRequest* key) {
- allRequests.erase(key);
- if (activeRequests.erase(key)) {
+ void remove(OnlineFileRequest* request) {
+ allRequests.erase(request);
+ if (activeRequests.erase(request)) {
activatePendingRequest();
} else {
- auto it = pendingRequestsMap.find(key);
+ auto it = pendingRequestsMap.find(request);
if (it != pendingRequestsMap.end()) {
pendingRequestsList.erase(it->second);
pendingRequestsMap.erase(it);
@@ -78,30 +77,30 @@ public:
}
}
- void activateOrQueueRequest(OnlineFileRequestImpl* impl) {
- assert(allRequests.find(impl->key) != allRequests.end());
- assert(activeRequests.find(impl->key) == activeRequests.end());
- assert(!impl->request);
+ void activateOrQueueRequest(OnlineFileRequest* request) {
+ assert(allRequests.find(request) != allRequests.end());
+ assert(activeRequests.find(request) == activeRequests.end());
+ assert(!request->request);
- if (activeRequests.size() >= HTTPContextBase::maximumConcurrentRequests()) {
- queueRequest(impl);
+ if (activeRequests.size() >= HTTPFileSource::maximumConcurrentRequests()) {
+ queueRequest(request);
} else {
- activateRequest(impl);
+ activateRequest(request);
}
}
- void queueRequest(OnlineFileRequestImpl* impl) {
- auto it = pendingRequestsList.insert(pendingRequestsList.end(), impl->key);
- pendingRequestsMap.emplace(impl->key, std::move(it));
+ void queueRequest(OnlineFileRequest* request) {
+ auto it = pendingRequestsList.insert(pendingRequestsList.end(), request);
+ pendingRequestsMap.emplace(request, std::move(it));
}
- void activateRequest(OnlineFileRequestImpl* impl) {
- activeRequests.insert(impl->key);
- impl->request = httpContext->createRequest(impl->resource, [=] (Response response) {
- impl->request = nullptr;
- activeRequests.erase(impl->key);
+ void activateRequest(OnlineFileRequest* request) {
+ activeRequests.insert(request);
+ request->request = httpFileSource.request(request->resource, [=] (Response response) {
+ activeRequests.erase(request);
activatePendingRequest();
- impl->completed(*this, response);
+ request->request.reset();
+ request->completed(response);
});
}
@@ -110,20 +109,18 @@ public:
return;
}
- FileRequest* key = pendingRequestsList.front();
+ OnlineFileRequest* request = pendingRequestsList.front();
pendingRequestsList.pop_front();
- pendingRequestsMap.erase(key);
+ pendingRequestsMap.erase(request);
- auto it = allRequests.find(key);
- assert(it != allRequests.end());
- activateRequest(it->second.get());
+ activateRequest(request);
}
private:
void networkIsReachableAgain() {
- for (auto& req : allRequests) {
- req.second->networkIsReachableAgain(*this);
+ for (auto& request : allRequests) {
+ request->networkIsReachableAgain();
}
}
@@ -138,23 +135,22 @@ private:
* Requests in any state are in `allRequests`. Requests in the pending state are in
* `pendingRequests`. Requests in the active state are in `activeRequests`.
*/
- std::unordered_map<FileRequest*, std::unique_ptr<OnlineFileRequestImpl>> allRequests;
- std::list<FileRequest*> pendingRequestsList;
- std::unordered_map<FileRequest*, std::list<FileRequest*>::iterator> pendingRequestsMap;
- std::unordered_set<FileRequest*> activeRequests;
+ std::unordered_set<OnlineFileRequest*> allRequests;
+ std::list<OnlineFileRequest*> pendingRequestsList;
+ std::unordered_map<OnlineFileRequest*, std::list<OnlineFileRequest*>::iterator> pendingRequestsMap;
+ std::unordered_set<OnlineFileRequest*> activeRequests;
- const std::unique_ptr<HTTPContextBase> httpContext { HTTPContextBase::createContext() };
+ HTTPFileSource httpFileSource;
util::AsyncTask reachability { std::bind(&Impl::networkIsReachableAgain, this) };
};
OnlineFileSource::OnlineFileSource()
- : thread(std::make_unique<util::Thread<Impl>>(
- util::ThreadContext{ "OnlineFileSource", util::ThreadType::Unknown, util::ThreadPriority::Low }, 0)) {
+ : impl(std::make_unique<Impl>()) {
}
OnlineFileSource::~OnlineFileSource() = default;
-std::unique_ptr<FileRequest> OnlineFileSource::request(const Resource& resource, Callback callback) {
+std::unique_ptr<AsyncRequest> OnlineFileSource::request(const Resource& resource, Callback callback) {
Resource res = resource;
switch (resource.kind) {
@@ -183,40 +179,25 @@ std::unique_ptr<FileRequest> OnlineFileSource::request(const Resource& resource,
break;
}
- class OnlineFileRequest : public FileRequest {
- public:
- OnlineFileRequest(Resource resource_, FileSource::Callback callback_, util::Thread<OnlineFileSource::Impl>& thread_)
- : thread(thread_),
- workRequest(thread.invokeWithCallback(&OnlineFileSource::Impl::request, callback_, this, resource_)) {
- }
-
- ~OnlineFileRequest() {
- thread.invoke(&OnlineFileSource::Impl::cancel, this);
- }
-
- util::Thread<OnlineFileSource::Impl>& thread;
- std::unique_ptr<WorkRequest> workRequest;
- };
-
- return std::make_unique<OnlineFileRequest>(res, callback, *thread);
+ return std::make_unique<OnlineFileRequest>(res, callback, *impl);
}
-OnlineFileRequestImpl::OnlineFileRequestImpl(FileRequest* key_, const Resource& resource_, Callback callback_, OnlineFileSource::Impl& impl)
- : key(key_),
+OnlineFileRequest::OnlineFileRequest(const Resource& resource_, Callback callback_, OnlineFileSource::Impl& impl_)
+ : impl(impl_),
resource(resource_),
callback(std::move(callback_)) {
+ impl.add(this);
+
// Force an immediate first request if we don't have an expiration time.
if (resource.priorExpires) {
- schedule(impl, resource.priorExpires);
+ schedule(resource.priorExpires);
} else {
- schedule(impl, SystemClock::now());
+ schedule(util::now());
}
}
-OnlineFileRequestImpl::~OnlineFileRequestImpl() {
- if (request) {
- request->cancel();
- }
+OnlineFileRequest::~OnlineFileRequest() {
+ impl.remove(this);
}
static Duration errorRetryTimeout(Response::Error::Reason failedRequestReason, uint32_t failedRequests) {
@@ -233,20 +214,20 @@ static Duration errorRetryTimeout(Response::Error::Reason failedRequestReason, u
}
}
-static Duration expirationTimeout(optional<SystemTimePoint> expires, uint32_t expiredRequests) {
+static Duration expirationTimeout(optional<Timestamp> expires, uint32_t expiredRequests) {
if (expiredRequests) {
return Seconds(1 << std::min(expiredRequests - 1, 31u));
} else if (expires) {
- return std::max(SystemDuration::zero(), *expires - SystemClock::now());
+ return std::max(Seconds::zero(), *expires - util::now());
} else {
return Duration::max();
}
}
-SystemTimePoint interpolateExpiration(const SystemTimePoint& current,
- optional<SystemTimePoint> prior,
- bool& expired) {
- auto now = SystemClock::now();
+Timestamp interpolateExpiration(const Timestamp& current,
+ optional<Timestamp> prior,
+ bool& expired) {
+ auto now = util::now();
if (current > now) {
return current;
}
@@ -275,10 +256,10 @@ SystemTimePoint interpolateExpiration(const SystemTimePoint& current,
// Assume that either the client or server clock is wrong and
// try to interpolate a valid expiration date (from the client POV)
// observing a minimum timeout.
- return now + std::max<SystemDuration>(delta, util::CLOCK_SKEW_RETRY_TIMEOUT);
+ return now + std::max<Seconds>(delta, util::CLOCK_SKEW_RETRY_TIMEOUT);
}
-void OnlineFileRequestImpl::schedule(OnlineFileSource::Impl& impl, optional<SystemTimePoint> expires) {
+void OnlineFileRequest::schedule(optional<Timestamp> expires) {
if (request) {
// There's already a request in progress; don't start another one.
return;
@@ -307,7 +288,7 @@ void OnlineFileRequestImpl::schedule(OnlineFileSource::Impl& impl, optional<Syst
});
}
-void OnlineFileRequestImpl::completed(OnlineFileSource::Impl& impl, Response response) {
+void OnlineFileRequest::completed(Response response) {
// If we didn't get various caching headers in the response, continue using the
// previous values. Otherwise, update the previous values to the new values.
@@ -345,15 +326,20 @@ void OnlineFileRequestImpl::completed(OnlineFileSource::Impl& impl, Response res
failedRequestReason = Response::Error::Reason::Success;
}
- callback(response);
- schedule(impl, response.expires);
+ schedule(response.expires);
+
+ // Calling the callback may result in `this` being deleted. It needs to be done last,
+ // and needs to make a local copy of the callback to ensure that it remains valid for
+ // the duration of the call.
+ auto callback_ = callback;
+ callback_(response);
}
-void OnlineFileRequestImpl::networkIsReachableAgain(OnlineFileSource::Impl& impl) {
+void OnlineFileRequest::networkIsReachableAgain() {
// We need all requests to fail at least once before we are going to start retrying
// them, and we only immediately restart request that failed due to connection issues.
if (failedRequestReason == Response::Error::Reason::Connection) {
- schedule(impl, SystemClock::now());
+ schedule(util::now());
}
}
diff --git a/platform/default/png_reader.cpp b/platform/default/png_reader.cpp
index 2b9bbbeb46..096596ee2e 100644
--- a/platform/default/png_reader.cpp
+++ b/platform/default/png_reader.cpp
@@ -83,8 +83,8 @@ PremultipliedImage decodePNG(const uint8_t* data, size_t size) {
png_set_sig_bytes(png_ptr, 8);
png_read_info(png_ptr, info_ptr);
- unsigned width = 0;
- unsigned height = 0;
+ png_uint_32 width = 0;
+ png_uint_32 height = 0;
int bit_depth = 0;
int color_type = 0;
png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, 0, 0, 0);
diff --git a/platform/default/sqlite3.cpp b/platform/default/sqlite3.cpp
index bf0bbfc683..b6db71a752 100644
--- a/platform/default/sqlite3.cpp
+++ b/platform/default/sqlite3.cpp
@@ -7,10 +7,7 @@
#include <chrono>
#include <experimental/optional>
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wunused-variable"
-// Check sqlite3 library version.
-const static bool sqliteVersionCheck = []() {
+const static bool sqliteVersionCheck __attribute__((unused)) = []() {
if (sqlite3_libversion_number() / 1000000 != SQLITE_VERSION_NUMBER / 1000000) {
char message[96];
snprintf(message, 96,
@@ -21,7 +18,6 @@ const static bool sqliteVersionCheck = []() {
return true;
}();
-#pragma GCC diagnostic pop
namespace mapbox {
namespace sqlite {
@@ -222,7 +218,9 @@ void Statement::bindBlob(int offset, const std::vector<uint8_t>& value, bool ret
bindBlob(offset, value.data(), value.size(), retain);
}
-template <> void Statement::bind(int offset, std::chrono::system_clock::time_point value) {
+template <>
+void Statement::bind(
+ int offset, std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds> value) {
assert(stmt);
check(sqlite3_bind_int64(stmt, offset, std::chrono::system_clock::to_time_t(value)));
}
@@ -235,7 +233,10 @@ template <> void Statement::bind(int offset, optional<std::string> value) {
}
}
-template <> void Statement::bind(int offset, optional<std::chrono::system_clock::time_point> value) {
+template <>
+void Statement::bind(
+ int offset,
+ optional<std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds>> value) {
if (!value) {
bind(offset, nullptr);
} else {
@@ -287,9 +288,12 @@ template <> std::vector<uint8_t> Statement::get(int offset) {
return { begin, end };
}
-template <> std::chrono::system_clock::time_point Statement::get(int offset) {
+template <>
+std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds>
+Statement::get(int offset) {
assert(stmt);
- return std::chrono::system_clock::from_time_t(sqlite3_column_int64(stmt, offset));
+ return std::chrono::time_point_cast<std::chrono::seconds>(
+ std::chrono::system_clock::from_time_t(sqlite3_column_int64(stmt, offset)));
}
template <> optional<int64_t> Statement::get(int offset) {
@@ -319,12 +323,15 @@ template <> optional<std::string> Statement::get(int offset) {
}
}
-template <> optional<std::chrono::system_clock::time_point> Statement::get(int offset) {
+template <>
+optional<std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds>>
+Statement::get(int offset) {
assert(stmt);
if (sqlite3_column_type(stmt, offset) == SQLITE_NULL) {
- return optional<std::chrono::system_clock::time_point>();
+ return {};
} else {
- return get<std::chrono::system_clock::time_point>(offset);
+ return get<std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds>>(
+ offset);
}
}
@@ -338,5 +345,40 @@ void Statement::clearBindings() {
sqlite3_clear_bindings(stmt);
}
+Transaction::Transaction(Database& db_, Mode mode)
+ : db(db_) {
+ switch (mode) {
+ case Deferred:
+ db.exec("BEGIN DEFERRED TRANSACTION");
+ break;
+ case Immediate:
+ db.exec("BEGIN IMMEDIATE TRANSACTION");
+ break;
+ case Exclusive:
+ db.exec("BEGIN EXCLUSIVE TRANSACTION");
+ break;
+ }
+}
+
+Transaction::~Transaction() {
+ if (needRollback) {
+ try {
+ rollback();
+ } catch (...) {
+ // Ignore failed rollbacks in destructor.
+ }
+ }
+}
+
+void Transaction::commit() {
+ needRollback = false;
+ db.exec("COMMIT TRANSACTION");
+}
+
+void Transaction::rollback() {
+ needRollback = false;
+ db.exec("ROLLBACK TRANSACTION");
+}
+
} // namespace sqlite
} // namespace mapbox
diff --git a/platform/default/sqlite3.hpp b/platform/default/sqlite3.hpp
index abe83a2d44..57ee18e9f3 100644
--- a/platform/default/sqlite3.hpp
+++ b/platform/default/sqlite3.hpp
@@ -88,5 +88,29 @@ private:
sqlite3_stmt *stmt = nullptr;
};
+class Transaction {
+private:
+ Transaction(const Transaction&) = delete;
+ Transaction(Transaction&&) = delete;
+ Transaction& operator=(const Transaction&) = delete;
+
+public:
+ enum Mode {
+ Deferred,
+ Immediate,
+ Exclusive
+ };
+
+ Transaction(Database&, Mode = Deferred);
+ ~Transaction();
+
+ void commit();
+ void rollback();
+
+private:
+ Database& db;
+ bool needRollback = true;
+};
+
}
}
diff --git a/platform/default/webp_reader.cpp b/platform/default/webp_reader.cpp
index 37d23da110..5b0eaf4741 100644
--- a/platform/default/webp_reader.cpp
+++ b/platform/default/webp_reader.cpp
@@ -15,8 +15,11 @@ PremultipliedImage decodeWebP(const uint8_t* data, size_t size) {
throw std::runtime_error("failed to retrieve WebP basic header information");
}
- std::unique_ptr<uint8_t[]> webp(WebPDecodeRGBA(data, size, &width, &height));
- if (!webp) {
+ int stride = width * 4;
+ size_t webpSize = stride * height;
+ auto webp = std::make_unique<uint8_t[]>(webpSize);
+
+ if (!WebPDecodeRGBAInto(data, size, webp.get(), webpSize, stride)) {
throw std::runtime_error("failed to decode WebP data");
}