-
Notifications
You must be signed in to change notification settings - Fork 1.3k
[ios, macos] Improve snap shotter documentation. #10020
Changes from all commits
5213133
64e0cc2
d16aea7
dff1bc0
b767c21
6ea1e41
6beeb87
9eaed01
21fe811
a072ecc
73f84e7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,19 +13,27 @@ | |
#import "MGLOfflineStorage_Private.h" | ||
#import "MGLGeometry_Private.h" | ||
#import "NSBundle+MGLAdditions.h" | ||
#import "MGLStyle.h" | ||
|
||
#if TARGET_OS_IPHONE | ||
#import "UIImage+MGLAdditions.h" | ||
#else | ||
#import "NSImage+MGLAdditions.h" | ||
#endif | ||
|
||
const CGPoint MGLLogoImagePosition = CGPointMake(8, 8); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It might be interesting to make this position configurable as tail work. |
||
const CGFloat MGLSnapshotterMinimumPixelSize = 64; | ||
|
||
@implementation MGLMapSnapshotOptions | ||
|
||
- (instancetype _Nonnull)initWithStyleURL:(NSURL* _Nonnull)styleURL camera:(MGLMapCamera*)camera size:(CGSize) size; | ||
- (instancetype _Nonnull)initWithStyleURL:(nullable NSURL*)styleURL camera:(MGLMapCamera*)camera size:(CGSize) size; | ||
{ | ||
self = [super init]; | ||
if (self) { | ||
if ( !styleURL) | ||
{ | ||
styleURL = [MGLStyle streetsStyleURLWithVersion:MGLStyleDefaultVersion]; | ||
} | ||
_styleURL = styleURL; | ||
_size = size; | ||
_camera = camera; | ||
|
@@ -41,28 +49,34 @@ - (instancetype _Nonnull)initWithStyleURL:(NSURL* _Nonnull)styleURL camera:(MGLM | |
|
||
@end | ||
|
||
@interface MGLMapSnapshotter() | ||
@property (nonatomic) MGLMapSnapshotOptions *options; | ||
@end | ||
|
||
@implementation MGLMapSnapshotter { | ||
|
||
std::shared_ptr<mbgl::ThreadPool> mbglThreadPool; | ||
std::unique_ptr<mbgl::MapSnapshotter> mbglMapSnapshotter; | ||
std::unique_ptr<mbgl::Actor<mbgl::MapSnapshotter::Callback>> snapshotCallback; | ||
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) { | ||
_options = options; | ||
_loading = false; | ||
|
||
mbgl::DefaultFileSource *mbglFileSource = [MGLOfflineStorage sharedOfflineStorage].mbglFileSource; | ||
mbglThreadPool = mbgl::sharedThreadPool(); | ||
_mbglThreadPool = mbgl::sharedThreadPool(); | ||
|
||
std::string styleURL = std::string([options.styleURL.absoluteString UTF8String]); | ||
|
||
// Size; taking into account the minimum texture size for OpenGL ES | ||
// For non retina screens the ratio is 1:1 MGLSnapshotterMinimumPixelSize | ||
mbgl::Size size = { | ||
static_cast<uint32_t>(MAX(options.size.width, 64)), | ||
static_cast<uint32_t>(MAX(options.size.height, 64)) | ||
static_cast<uint32_t>(MAX(options.size.width, MGLSnapshotterMinimumPixelSize)), | ||
static_cast<uint32_t>(MAX(options.size.height, MGLSnapshotterMinimumPixelSize)) | ||
}; | ||
|
||
float pixelRatio = MAX(options.scale, 1); | ||
|
@@ -73,17 +87,17 @@ - (instancetype)initWithOptions:(MGLMapSnapshotOptions*)options; | |
cameraOptions.center = MGLLatLngFromLocationCoordinate2D(options.camera.centerCoordinate); | ||
} | ||
cameraOptions.angle = MAX(0, options.camera.heading) * mbgl::util::DEG2RAD; | ||
cameraOptions.zoom = MAX(0, options.zoom); | ||
cameraOptions.zoom = MAX(0, options.zoomLevel); | ||
cameraOptions.pitch = MAX(0, options.camera.pitch); | ||
|
||
// Region | ||
mbgl::optional<mbgl::LatLngBounds> region; | ||
if (!MGLCoordinateBoundsIsEmpty(options.region)) { | ||
region = MGLLatLngBoundsFromCoordinateBounds(options.region); | ||
mbgl::optional<mbgl::LatLngBounds> coordinateBounds; | ||
if (!MGLCoordinateBoundsIsEmpty(options.coordinateBounds)) { | ||
coordinateBounds = MGLLatLngBoundsFromCoordinateBounds(options.coordinateBounds); | ||
} | ||
|
||
// Create the snapshotter | ||
mbglMapSnapshotter = std::make_unique<mbgl::MapSnapshotter>(*mbglFileSource, *mbglThreadPool, styleURL, size, pixelRatio, cameraOptions, region); | ||
_mbglMapSnapshotter = std::make_unique<mbgl::MapSnapshotter>(*mbglFileSource, *_mbglThreadPool, styleURL, size, pixelRatio, cameraOptions, coordinateBounds); | ||
} | ||
return self; | ||
} | ||
|
@@ -96,51 +110,61 @@ - (void)startWithCompletionHandler:(MGLMapSnapshotCompletionHandler)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; | ||
[NSException raise:NSInternalInconsistencyException | ||
format:@"Already started this snapshotter."]; | ||
} | ||
|
||
_loading = true; | ||
|
||
dispatch_async(queue, ^{ | ||
snapshotCallback = std::make_unique<mbgl::Actor<mbgl::MapSnapshotter::Callback>>(*mbgl::Scheduler::GetCurrent(), [=](std::exception_ptr mbglError, mbgl::PremultipliedImage image) { | ||
_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]; | ||
NSError *error = [NSError errorWithDomain:MGLErrorDomain code:MGLErrorCodeSnapshotFailed userInfo:userInfo]; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Per #10020 (comment), should we remove this error or replace it with an NSException? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it makes sense let the developer know that it has a snapshotter task running it's easy to forget this. A NSException is too drastic, I would like to continue with the NSError for the time being and evaluate this again once we made the snapshotter reusable. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To elaborate, NSError is intended to problems outside the developer’s control, for issues that can’t be fixed in the developer’s own code. If the developer is reusing a snapshotter in their own code, I think that would be an error on their part and an appropriate use of NSException. But if MapKit simply sets the NSError in this case, then I guess it’s OK to follow their lead. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree with you NSError is intended to problems outside the developer's control. NSException is a better choice. Making the changes. |
||
|
||
// Dispatch result to origin queue | ||
dispatch_async(queue, ^{ | ||
completion(nil, error); | ||
}); | ||
} else { | ||
#if TARGET_OS_IPHONE | ||
MGLImage *mglImage = [[MGLImage alloc] initWithMGLPremultipliedImage:std::move(image) scale:self.options.scale]; | ||
#else | ||
MGLImage *mglImage = [[MGLImage alloc] initWithMGLPremultipliedImage:std::move(image)]; | ||
#endif | ||
|
||
// 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); | ||
UIGraphicsBeginImageContextWithOptions(mglImage.size, NO, self.options.scale); | ||
|
||
[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)]; | ||
[logoImage drawInRect:CGRectMake(MGLLogoImagePosition.x, mglImage.size.height - (MGLLogoImagePosition.y + 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; | ||
NSImage *sourceImage = mglImage; | ||
|
||
NSSize targetSize = NSMakeSize(self.options.size.width, self.options.size.height); | ||
NSRect targetFrame = NSMakeRect(0, 0, targetSize.width, targetSize.height); | ||
NSImage *compositedImage = nil; | ||
NSImageRep *sourceImageRep = [sourceImage bestRepresentationForRect:targetFrame | ||
context:nil | ||
hints:nil]; | ||
compositedImage = [[NSImage alloc] initWithSize:targetSize]; | ||
|
||
[compositedImage lockFocus]; | ||
[logoImage drawInRect:CGRectMake(8, 8, logoImage.size.width,logoImage.size.height)]; | ||
[sourceImageRep drawInRect: targetFrame]; | ||
[logoImage drawInRect:CGRectMake(MGLLogoImagePosition.x, MGLLogoImagePosition.y, logoImage.size.width,logoImage.size.height)]; | ||
[compositedImage unlockFocus]; | ||
|
||
#endif | ||
|
||
// Dispatch result to origin queue | ||
|
@@ -150,14 +174,14 @@ - (void)startWithQueue:(dispatch_queue_t)queue completionHandler:(MGLMapSnapshot | |
}); | ||
} | ||
}); | ||
mbglMapSnapshotter->snapshot(snapshotCallback->self()); | ||
_mbglMapSnapshotter->snapshot(_snapshotCallback->self()); | ||
}); | ||
} | ||
|
||
- (void)cancel; | ||
{ | ||
snapshotCallback.reset(); | ||
mbglMapSnapshotter.reset(); | ||
_snapshotCallback.reset(); | ||
_mbglMapSnapshotter.reset(); | ||
} | ||
|
||
@end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.