summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIvo van Dongen <info@ivovandongen.nl>2017-08-30 17:22:48 +0300
committerIvo van Dongen <info@ivovandongen.nl>2017-09-05 15:54:12 +0300
commit9343978d280a3fe9fbe48f2bab685686f819d55b (patch)
tree1eca4447b05c3fbb0eb1b57ea22e1f865c5c4abf
parentb74470fb30fc92864ef3229d899cf600e46bd270 (diff)
downloadqtlocation-mapboxgl-9343978d280a3fe9fbe48f2bab685686f819d55b.tar.gz
[darwin] snapshotter
-rw-r--r--platform/darwin/src/MGLMapSnapshotter.h105
-rw-r--r--platform/darwin/src/MGLMapSnapshotter.mm163
2 files changed, 268 insertions, 0 deletions
diff --git a/platform/darwin/src/MGLMapSnapshotter.h b/platform/darwin/src/MGLMapSnapshotter.h
new file mode 100644
index 0000000000..a2a4f1b331
--- /dev/null
+++ b/platform/darwin/src/MGLMapSnapshotter.h
@@ -0,0 +1,105 @@
+#import <Foundation/Foundation.h>
+#import "MGLTypes.h"
+#import "MGLGeometry.h"
+#import "MGLMapCamera.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+MGL_EXPORT
+/**
+ The options to use when creating images with the `MGLMapsnapshotter`.
+ */
+@interface MGLMapSnapshotOptions : NSObject
+
+/**
+ Creates a set of options with the minimum required information
+ @param styleURL the style url to use
+ @param camera the camera settings
+ @param size the image size
+ */
+- (instancetype)initWithStyleURL:(NSURL*)styleURL camera:(MGLMapCamera*)camera size:(CGSize)size;
+
+#pragma mark - Configuring the map
+
+/**
+ The style URL for these options.
+ */
+@property (nonatomic, readonly) NSURL* styleURL;
+
+/**
+ The zoom. Default is 0.
+ */
+@property (nonatomic) double zoom;
+
+/**
+ The `MGLMapcamera` options to use.
+ */
+@property (nonatomic) MGLMapCamera* camera;
+
+/**
+ A region to capture. Overrides the center coordinate
+ in the mapCamera options if set
+ */
+@property (nonatomic) MGLCoordinateBounds region;
+
+#pragma mark - Configuring the image
+
+/**
+ The size of the output image. Minimum is 64x64
+ */
+@property (nonatomic, readonly) CGSize size;
+
+/**
+ The scale of the output image. Defaults to the main screen scale.
+ Minimum is 1.
+ */
+@property (nonatomic) CGFloat scale;
+
+@end
+
+/**
+ A block to processes the result or error of a snapshot request.
+
+ The result will be either an `MGLImage` or a `NSError`
+
+ @param snapshot The image that was generated or `nil` if an error occurred.
+ @param error The eror that occured or `nil` when succesful.
+ */
+typedef void (^MGLMapSnapshotCompletionHandler)(MGLImage* _Nullable snapshot, NSError* _Nullable error);
+
+/**
+ A utility object for capturing map-based images.
+ */
+MGL_EXPORT
+@interface MGLMapSnapshotter : NSObject
+
+- (instancetype)initWithOptions:(MGLMapSnapshotOptions*)options;
+
+/**
+ Starts the snapshot creation and executes the specified block with the result.
+
+ @param completionHandler The block to handle the result in.
+ */
+- (void)startWithCompletionHandler:(MGLMapSnapshotCompletionHandler)completionHandler;
+
+/**
+ Starts the snapshot creation and executes the specified block with the result on the specified queue.
+
+ @param queue The queue to handle the result on.
+ @param completionHandler The block to handle the result in.
+ */
+- (void)startWithQueue:(dispatch_queue_t)queue completionHandler:(MGLMapSnapshotCompletionHandler)completionHandler;
+
+/**
+ Cancels the snapshot creation request, if any.
+ */
+- (void)cancel;
+
+/**
+ Indicates whether as snapshot is currently being generated.
+ */
+@property (nonatomic, readonly, getter=isLoading) BOOL loading;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/platform/darwin/src/MGLMapSnapshotter.mm b/platform/darwin/src/MGLMapSnapshotter.mm
new file mode 100644
index 0000000000..c81fd39c4a
--- /dev/null
+++ b/platform/darwin/src/MGLMapSnapshotter.mm
@@ -0,0 +1,163 @@
+#import "MGLMapSnapshotter.h"
+
+#import <mbgl/actor/actor.hpp>
+#import <mbgl/actor/scheduler.hpp>
+#import <mbgl/util/geo.hpp>
+#import <mbgl/map/map_snapshotter.hpp>
+#import <mbgl/map/camera.hpp>
+#import <mbgl/storage/default_file_source.hpp>
+#import <mbgl/util/default_thread_pool.hpp>
+#import <mbgl/util/string.hpp>
+#import <mbgl/util/shared_thread_pool.hpp>
+
+#import "MGLOfflineStorage_Private.h"
+#import "MGLGeometry_Private.h"
+#import "NSBundle+MGLAdditions.h"
+
+#if TARGET_OS_IPHONE
+#import "UIImage+MGLAdditions.h"
+#else
+#import "NSImage+MGLAdditions.h"
+#endif
+
+@implementation MGLMapSnapshotOptions
+
+- (instancetype _Nonnull)initWithStyleURL:(NSURL* _Nonnull)styleURL camera:(MGLMapCamera*)camera size:(CGSize) size;
+{
+ self = [super init];
+ if (self) {
+ _styleURL = styleURL;
+ _size = size;
+ _camera = camera;
+#if TARGET_OS_IPHONE
+ _scale = [UIScreen mainScreen].scale;
+#else
+ _scale = [NSScreen mainScreen].backingScaleFactor;
+#endif
+
+ }
+ return self;
+}
+
+@end
+
+@implementation MGLMapSnapshotter {
+
+ std::shared_ptr<mbgl::ThreadPool> mbglThreadPool;
+ std::unique_ptr<mbgl::MapSnapshotter> mbglMapSnapshotter;
+ std::unique_ptr<mbgl::Actor<mbgl::MapSnapshotter::Callback>> snapshotCallback;
+}
+
+- (instancetype)initWithOptions:(MGLMapSnapshotOptions*)options;
+{
+ self = [super init];
+ if (self) {
+ _loading = false;
+
+ mbgl::DefaultFileSource *mbglFileSource = [MGLOfflineStorage sharedOfflineStorage].mbglFileSource;
+ mbglThreadPool = mbgl::sharedThreadPool();
+
+ std::string styleURL = std::string([options.styleURL.absoluteString UTF8String]);
+
+ // Size; taking into account the minimum texture size for OpenGL ES
+ mbgl::Size size = {
+ static_cast<uint32_t>(MAX(options.size.width, 64)),
+ static_cast<uint32_t>(MAX(options.size.height, 64))
+ };
+
+ float pixelRatio = MAX(options.scale, 1);
+
+ // Camera options
+ mbgl::CameraOptions cameraOptions;
+ if (CLLocationCoordinate2DIsValid(options.camera.centerCoordinate)) {
+ cameraOptions.center = MGLLatLngFromLocationCoordinate2D(options.camera.centerCoordinate);
+ }
+ cameraOptions.angle = MAX(0, options.camera.heading) * mbgl::util::DEG2RAD;
+ cameraOptions.zoom = MAX(0, options.zoom);
+ cameraOptions.pitch = MAX(0, options.camera.pitch);
+
+ // Region
+ mbgl::optional<mbgl::LatLngBounds> region;
+ if (!MGLCoordinateBoundsIsEmpty(options.region)) {
+ region = MGLLatLngBoundsFromCoordinateBounds(options.region);
+ }
+
+ // Create the snapshotter
+ mbglMapSnapshotter = std::make_unique<mbgl::MapSnapshotter>(*mbglFileSource, *mbglThreadPool, styleURL, size, pixelRatio, cameraOptions, region);
+ }
+ return self;
+}
+
+- (void)startWithCompletionHandler:(MGLMapSnapshotCompletionHandler)completion;
+{
+ [self startWithQueue:dispatch_get_main_queue() completionHandler:completion];
+}
+
+- (void)startWithQueue:(dispatch_queue_t)queue completionHandler:(MGLMapSnapshotCompletionHandler)completion;
+{
+ if ([self isLoading]) {
+ NSDictionary *userInfo = @{NSLocalizedDescriptionKey: @"Already started this snapshotter"};
+ NSError *error = [NSError errorWithDomain:MGLErrorDomain code:1 userInfo:userInfo];
+ dispatch_async(queue, ^{
+ completion(nil, error);
+ });
+ return;
+ }
+
+ _loading = true;
+
+ dispatch_async(queue, ^{
+ snapshotCallback = std::make_unique<mbgl::Actor<mbgl::MapSnapshotter::Callback>>(*mbgl::Scheduler::GetCurrent(), [=](std::exception_ptr mbglError, mbgl::PremultipliedImage image) {
+ _loading = false;
+ if (mbglError) {
+ NSString *description = @(mbgl::util::toString(mbglError).c_str());
+ NSDictionary *userInfo = @{NSLocalizedDescriptionKey: description};
+ NSError *error = [NSError errorWithDomain:MGLErrorDomain code:1 userInfo:userInfo];
+
+ // Dispatch result to origin queue
+ dispatch_async(queue, ^{
+ completion(nil, error);
+ });
+ } else {
+ MGLImage *mglImage = [[MGLImage alloc] initWithMGLPremultipliedImage:std::move(image)];
+
+ // Process image watermark in a work queue
+ dispatch_queue_t workQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
+ dispatch_async(workQueue, ^{
+#if TARGET_OS_IPHONE
+ UIImage *logoImage = [UIImage imageNamed:@"mapbox" inBundle:[NSBundle mgl_frameworkBundle] compatibleWithTraitCollection:nil];
+
+ UIGraphicsBeginImageContext(mglImage.size);
+
+ [mglImage drawInRect:CGRectMake(0, 0, mglImage.size.width, mglImage.size.height)];
+ [logoImage drawInRect:CGRectMake(8, mglImage.size.height - (8 + logoImage.size.height), logoImage.size.width,logoImage.size.height)];
+ UIImage *compositedImage = UIGraphicsGetImageFromCurrentImageContext();
+
+ UIGraphicsEndImageContext();
+#else
+ NSImage *logoImage = [[NSImage alloc] initWithContentsOfFile:[[NSBundle mgl_frameworkBundle] pathForResource:@"mapbox" ofType:@"pdf"]];
+ NSImage *compositedImage = mglImage;
+
+ [compositedImage lockFocus];
+ [logoImage drawInRect:CGRectMake(8, 8, logoImage.size.width,logoImage.size.height)];
+ [compositedImage unlockFocus];
+#endif
+
+ // Dispatch result to origin queue
+ dispatch_async(queue, ^{
+ completion(compositedImage, nil);
+ });
+ });
+ }
+ });
+ mbglMapSnapshotter->snapshot(snapshotCallback->self());
+ });
+}
+
+- (void)cancel;
+{
+ snapshotCallback.reset();
+ mbglMapSnapshotter.reset();
+}
+
+@end