summaryrefslogtreecommitdiff
path: root/platform/darwin/src/MGLMapSnapshotter.mm
diff options
context:
space:
mode:
Diffstat (limited to 'platform/darwin/src/MGLMapSnapshotter.mm')
-rw-r--r--platform/darwin/src/MGLMapSnapshotter.mm105
1 files changed, 81 insertions, 24 deletions
diff --git a/platform/darwin/src/MGLMapSnapshotter.mm b/platform/darwin/src/MGLMapSnapshotter.mm
index d9d16cc5be..aa1a988b1b 100644
--- a/platform/darwin/src/MGLMapSnapshotter.mm
+++ b/platform/darwin/src/MGLMapSnapshotter.mm
@@ -109,7 +109,10 @@ const CGFloat MGLSnapshotterMinimumPixelSize = 64;
@end
@interface MGLMapSnapshotter()
-@property (nonatomic) BOOL loading;
+@property (nonatomic) BOOL cancelled;
+@property (nonatomic) dispatch_queue_t resultQueue;
+@property (nonatomic, copy) MGLMapSnapshotCompletionHandler completion;
++ (void)completeWithErrorCode:(MGLErrorCode)errorCode description:(nonnull NSString*)description onQueue:(dispatch_queue_t)queue completion:(MGLMapSnapshotCompletionHandler)completion;
@end
@implementation MGLMapSnapshotter {
@@ -118,13 +121,22 @@ const CGFloat MGLSnapshotterMinimumPixelSize = 64;
std::unique_ptr<mbgl::Actor<mbgl::MapSnapshotter::Callback>> _snapshotCallback;
}
+- (void)dealloc {
+ if (_completion) {
+ NSAssert(_snapshotCallback, @"Snapshot in progress - there should be a valid callback");
+
+ [MGLMapSnapshotter completeWithErrorCode:MGLErrorCodeSnapshotFailed
+ description:@"MGLMapSnapshotter deallocated prior to snapshot completion."
+ onQueue:_resultQueue
+ completion:_completion];
+ }
+}
+
- (instancetype)initWithOptions:(MGLMapSnapshotOptions *)options
{
self = [super init];
if (self) {
[self setOptions:options];
- _loading = false;
-
}
return self;
}
@@ -141,13 +153,16 @@ const CGFloat MGLSnapshotterMinimumPixelSize = 64;
format:@"startWithQueue:completionHandler: must be called from a thread with an active run loop."];
}
- if ([self isLoading]) {
+ if (self.completion) {
+ // Consider replacing this exception with an error passed to the completion block.
[NSException raise:NSInternalInconsistencyException
format:@"Already started this snapshotter."];
}
- self.loading = true;
-
+ self.completion = completion;
+ self.resultQueue = queue;
+ self.cancelled = NO;
+
__weak __typeof__(self) weakSelf = self;
// mbgl::Scheduler::GetCurrent() scheduler means "run callback on current (ie UI/main) thread"
// capture weakSelf to avoid retain cycle if callback is never called (ie snapshot cancelled)
@@ -160,16 +175,18 @@ const CGFloat MGLSnapshotterMinimumPixelSize = 64;
// If self had died, _snapshotCallback would have been destroyed and this block would not be executed
NSCAssert(strongSelf, @"Snapshot callback executed after being destroyed.");
- strongSelf.loading = false;
+ if (!strongSelf.completion)
+ return;
if (mbglError) {
NSString *description = @(mbgl::util::toString(mbglError).c_str());
NSDictionary *userInfo = @{NSLocalizedDescriptionKey: description};
NSError *error = [NSError errorWithDomain:MGLErrorDomain code:MGLErrorCodeSnapshotFailed userInfo:userInfo];
- // Dispatch result to origin queue
+ // Dispatch to result queue
dispatch_async(queue, ^{
- completion(nil, error);
+ strongSelf.completion(nil, error);
+ strongSelf.completion = nil;
});
} else {
#if TARGET_OS_IPHONE
@@ -179,9 +196,10 @@ const CGFloat MGLSnapshotterMinimumPixelSize = 64;
mglImage.size = NSMakeSize(mglImage.size.width / strongSelf.options.scale,
mglImage.size.height / strongSelf.options.scale);
#endif
- [strongSelf drawAttributedSnapshot:attributions snapshotImage:mglImage pointForFn:pointForFn latLngForFn:latLngForFn queue:queue completionHandler:completion];
+ [strongSelf drawAttributedSnapshot:attributions snapshotImage:mglImage pointForFn:pointForFn latLngForFn:latLngForFn];
}
strongSelf->_snapshotCallback = NULL;
+
});
// Launches snapshot on background Thread owned by mbglMapSnapshotter
@@ -190,10 +208,10 @@ const CGFloat MGLSnapshotterMinimumPixelSize = 64;
_mbglMapSnapshotter->snapshot(_snapshotCallback->self());
}
-+ (void)drawAttributedSnapshotWorker:(mbgl::MapSnapshotter::Attributions)attributions snapshotImage:(MGLImage *)mglImage pointForFn:(mbgl::MapSnapshotter::PointForFn)pointForFn latLngForFn:(mbgl::MapSnapshotter::LatLngForFn)latLngForFn queue:(dispatch_queue_t)queue scale:(CGFloat)scale size:(CGSize)size completionHandler:(MGLMapSnapshotCompletionHandler)completion {
-
++ (MGLImage*)drawAttributedSnapshotWorker:(mbgl::MapSnapshotter::Attributions)attributions snapshotImage:(MGLImage *)mglImage pointForFn:(mbgl::MapSnapshotter::PointForFn)pointForFn latLngForFn:(mbgl::MapSnapshotter::LatLngForFn)latLngForFn scale:(CGFloat)scale size:(CGSize)size {
+
NSArray<MGLAttributionInfo *>* attributionInfo = [MGLMapSnapshotter generateAttributionInfos:attributions];
-
+
#if TARGET_OS_IPHONE
MGLAttributionInfoStyle attributionInfoStyle = MGLAttributionInfoStyleLong;
for (NSUInteger styleValue = MGLAttributionInfoStyleLong; styleValue >= MGLAttributionInfoStyleShort; styleValue--) {
@@ -251,7 +269,11 @@ const CGFloat MGLSnapshotterMinimumPixelSize = 64;
UIImage *compositedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
+
+ return compositedImage;
+
#else
+
NSSize targetSize = NSMakeSize(size.width, size.height);
NSRect targetFrame = NSMakeRect(0, 0, targetSize.width, targetSize.height);
@@ -310,29 +332,41 @@ const CGFloat MGLSnapshotterMinimumPixelSize = 64;
[MGLMapSnapshotter drawAttributionTextWithStyle:attributionInfoStyle origin:attributionTextPosition attributionInfo:attributionInfo];
[compositedImage unlockFocus];
-
+
+ return compositedImage;
#endif
- // Dispatch result to origin queue
- dispatch_async(queue, ^{
- MGLMapSnapshot* snapshot = [[MGLMapSnapshot alloc] initWithImage:compositedImage
- scale:scale
- pointForFn:pointForFn
- latLngForFn:latLngForFn];
- completion(snapshot, nil);
- });
}
-- (void)drawAttributedSnapshot:(mbgl::MapSnapshotter::Attributions)attributions snapshotImage:(MGLImage *)mglImage pointForFn:(mbgl::MapSnapshotter::PointForFn)pointForFn latLngForFn:(mbgl::MapSnapshotter::LatLngForFn)latLngForFn queue:(dispatch_queue_t)queue completionHandler:(MGLMapSnapshotCompletionHandler)completion {
+- (void)drawAttributedSnapshot:(mbgl::MapSnapshotter::Attributions)attributions snapshotImage:(MGLImage *)mglImage pointForFn:(mbgl::MapSnapshotter::PointForFn)pointForFn latLngForFn:(mbgl::MapSnapshotter::LatLngForFn)latLngForFn {
// Process image watermark in a work queue
dispatch_queue_t workQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
+ dispatch_queue_t resultQueue = self.resultQueue;
+
// Capture scale and size by value to avoid accessing self from another thread
CGFloat scale = self.options.scale;
CGSize size = self.options.size;
+
// pointForFn is a copyable std::function that captures state by value: see MapSnapshotter::Impl::snapshot
+ __weak __typeof__(self) weakself = self;
+
dispatch_async(workQueue, ^{
// Call a class method to ensure we're not accidentally capturing self
- [MGLMapSnapshotter drawAttributedSnapshotWorker:attributions snapshotImage:mglImage pointForFn:pointForFn latLngForFn:latLngForFn queue:queue scale:scale size:size completionHandler:completion];
+ MGLImage *compositedImage = [MGLMapSnapshotter drawAttributedSnapshotWorker:attributions snapshotImage:mglImage pointForFn:pointForFn latLngForFn:latLngForFn scale:scale size:size];
+
+ // Dispatch result to origin queue
+ dispatch_async(resultQueue, ^{
+ __typeof__(self) strongself = weakself;
+
+ if (strongself.completion) {
+ MGLMapSnapshot* snapshot = [[MGLMapSnapshot alloc] initWithImage:compositedImage
+ scale:scale
+ pointForFn:pointForFn
+ latLngForFn:latLngForFn];
+ strongself.completion(snapshot, nil);
+ strongself.completion = nil;
+ }
+ });
});
}
@@ -466,10 +500,33 @@ const CGFloat MGLSnapshotterMinimumPixelSize = 64;
- (void)cancel
{
+ self.cancelled = YES;
+
+ if (_snapshotCallback) {
+ [MGLMapSnapshotter completeWithErrorCode:MGLErrorCodeSnapshotFailed
+ description:[NSString stringWithFormat:@"MGLMapSnapshotter cancelled from %s", __PRETTY_FUNCTION__]
+ onQueue:self.resultQueue
+ completion:self.completion];
+ self.completion = nil;
+ }
+
_snapshotCallback.reset();
_mbglMapSnapshotter.reset();
}
++ (void)completeWithErrorCode:(MGLErrorCode)errorCode description:(nonnull NSString*)description onQueue:(dispatch_queue_t)queue completion:(MGLMapSnapshotCompletionHandler)completion {
+ // The snapshot hasn't completed, so we should alert the caller
+ if (completion && queue) {
+ dispatch_async(queue, ^{
+ NSDictionary *userInfo = @{NSLocalizedDescriptionKey: description};
+ NSError *error = [NSError errorWithDomain:MGLErrorDomain
+ code:errorCode
+ userInfo:userInfo];
+ completion(NULL, error);
+ });
+ }
+}
+
- (void)setOptions:(MGLMapSnapshotOptions *)options
{
_options = options;