From 3be0f9bc269804781a44bfcf294e22878bef9cb4 Mon Sep 17 00:00:00 2001 From: Nadia Barbosa Date: Mon, 9 Sep 2019 16:17:58 -0700 Subject: [ios, macos] Introduce custom drawing overlays for MGLMapSnapshotter --- platform/darwin/src/MGLMapSnapshotter.h | 33 ++++++++ platform/darwin/src/MGLMapSnapshotter.mm | 79 ++++++++++++++++--- platform/ios/CHANGELOG.md | 3 + .../Snapshotter Tests/MGLMapSnapshotterTest.m | 57 ++++++++++++++ platform/ios/MBXContextSnapshotViewController.m | 77 ++++++++++++++++++ .../ios/app/MBXContextSnapshotViewController.h | 17 ++++ .../ios/app/MBXContextSnapshotViewController.m | 92 ++++++++++++++++++++++ platform/ios/app/Main.storyboard | 25 ++++-- platform/ios/ios.xcodeproj/project.pbxproj | 6 ++ platform/macos/CHANGELOG.md | 1 + 10 files changed, 374 insertions(+), 16 deletions(-) create mode 100644 platform/ios/MBXContextSnapshotViewController.m create mode 100644 platform/ios/app/MBXContextSnapshotViewController.h create mode 100644 platform/ios/app/MBXContextSnapshotViewController.m diff --git a/platform/darwin/src/MGLMapSnapshotter.h b/platform/darwin/src/MGLMapSnapshotter.h index 1ee9bd99bb..79767a8110 100644 --- a/platform/darwin/src/MGLMapSnapshotter.h +++ b/platform/darwin/src/MGLMapSnapshotter.h @@ -5,6 +5,27 @@ NS_ASSUME_NONNULL_BEGIN +MGL_EXPORT + +/** + An overlay that is placed within a `MGLMapSnapshot`. + + To access this object, use `-[MGLMapSnapshotter startWithOverlayHandler:completionHandler:]`. + */ + +@interface MGLMapSnapshotOverlay : NSObject + +/** + The current `CGContext` that snapshot is drawing within. You may use this context + to perform additional custom drawing. + */ +@property CGContextRef context; + +@end + + +typedef void (^MGLMapSnapshotOverlayHandler)(MGLMapSnapshotOverlay * _Nullable snapshotOverlay); + /** The options to use when creating images with the `MGLMapSnapshotter`. */ @@ -200,6 +221,18 @@ MGL_EXPORT */ - (void)startWithQueue:(dispatch_queue_t)queue completionHandler:(MGLMapSnapshotCompletionHandler)completionHandler; + +/* + Starts the snapshot creation and executes the specified blocks with the result + on the specified queue. Use this option if you want to add custom drawing on top of the + resulting `MGLMapSnapShot`. + + @param queue The queue to handle the result on. + @param overlayHandler The block to handle manipulation of the `MGLMapSnapshotter`'s `CGContext`. + @param completionHandler The block to handle the result in. + */ +- (void)startWithOverlayHandler:(MGLMapSnapshotOverlayHandler)overlayHandler completionHandler:(MGLMapSnapshotCompletionHandler)completionHandler; + /** Cancels the snapshot creation request, if any. diff --git a/platform/darwin/src/MGLMapSnapshotter.mm b/platform/darwin/src/MGLMapSnapshotter.mm index 0001ebcb86..cd0ede0a97 100644 --- a/platform/darwin/src/MGLMapSnapshotter.mm +++ b/platform/darwin/src/MGLMapSnapshotter.mm @@ -32,6 +32,26 @@ const CGPoint MGLLogoImagePosition = CGPointMake(8, 8); const CGFloat MGLSnapshotterMinimumPixelSize = 64; + +@interface MGLMapSnapshotOverlay() + +- (instancetype)initWithContext:(CGContextRef)context; + +@end + +@implementation MGLMapSnapshotOverlay + +- (instancetype) initWithContext:(CGContextRef)context { + self = [super init]; + if (self) { + _context = context; + } + + return self; +} + +@end + @implementation MGLMapSnapshotOptions - (instancetype _Nonnull)initWithStyleURL:(nullable NSURL *)styleURL camera:(MGLMapCamera *)camera size:(CGSize)size @@ -183,7 +203,15 @@ const CGFloat MGLSnapshotterMinimumPixelSize = 64; [self startWithQueue:dispatch_get_main_queue() completionHandler:completion]; } -- (void)startWithQueue:(dispatch_queue_t)queue completionHandler:(MGLMapSnapshotCompletionHandler)completion +- (void)startWithQueue:(dispatch_queue_t)queue completionHandler:(MGLMapSnapshotCompletionHandler)completionHandler { + [self startWithQueue:queue overlayHandler:nil completionHandler:completionHandler]; +} + +- (void)startWithOverlayHandler:(MGLMapSnapshotOverlayHandler)overlayHandler completionHandler:(MGLMapSnapshotCompletionHandler)completion { + [self startWithQueue:dispatch_get_main_queue() overlayHandler:overlayHandler completionHandler:completion]; +} + +- (void)startWithQueue:(dispatch_queue_t)queue overlayHandler:(MGLMapSnapshotOverlayHandler)overlayHandler completionHandler:(MGLMapSnapshotCompletionHandler)completion { if (!mbgl::Scheduler::GetCurrent()) { [NSException raise:NSInvalidArgumentException @@ -206,6 +234,7 @@ const CGFloat MGLSnapshotterMinimumPixelSize = 64; 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) @@ -238,7 +267,8 @@ 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]; + + [strongSelf drawAttributedSnapshot:attributions snapshotImage:mglImage pointForFn:pointForFn latLngForFn:latLngForFn overlayHandler:overlayHandler]; } strongSelf->_snapshotCallback = NULL; @@ -250,7 +280,7 @@ const CGFloat MGLSnapshotterMinimumPixelSize = 64; _mbglMapSnapshotter->snapshot(_snapshotCallback->self()); } -+ (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 { ++ (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 overlayHandler:(MGLMapSnapshotOverlayHandler)overlayHandler { NSArray* attributionInfo = [MGLMapSnapshotter generateAttributionInfos:attributions]; @@ -292,6 +322,22 @@ const CGFloat MGLSnapshotterMinimumPixelSize = 64; UIGraphicsBeginImageContextWithOptions(mglImage.size, NO, scale); [mglImage drawInRect:CGRectMake(0, 0, mglImage.size.width, mglImage.size.height)]; + + CGContextRef currentContext = UIGraphicsGetCurrentContext(); + + if (currentContext && overlayHandler) { + MGLMapSnapshotOverlay *snapshotOverlay = [[MGLMapSnapshotOverlay alloc] initWithContext:currentContext]; + CGContextSaveGState(snapshotOverlay.context); + overlayHandler(snapshotOverlay); + CGContextRestoreGState(snapshotOverlay.context); + currentContext = UIGraphicsGetCurrentContext(); + } + + if (!currentContext && overlayHandler) { + // If the current context has been corrupted by the user, + // return nil so we can generate an error later. + return nil; + } [logoImage drawInRect:logoImageRect]; @@ -379,7 +425,7 @@ const CGFloat MGLSnapshotterMinimumPixelSize = 64; #endif } -- (void)drawAttributedSnapshot:(mbgl::MapSnapshotter::Attributions)attributions snapshotImage:(MGLImage *)mglImage pointForFn:(mbgl::MapSnapshotter::PointForFn)pointForFn latLngForFn:(mbgl::MapSnapshotter::LatLngForFn)latLngForFn { +- (void)drawAttributedSnapshot:(mbgl::MapSnapshotter::Attributions)attributions snapshotImage:(MGLImage *)mglImage pointForFn:(mbgl::MapSnapshotter::PointForFn)pointForFn latLngForFn:(mbgl::MapSnapshotter::LatLngForFn)latLngForFn overlayHandler:(MGLMapSnapshotOverlayHandler)overlayHandler { // Process image watermark in a work queue dispatch_queue_t workQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); @@ -394,19 +440,30 @@ const CGFloat MGLSnapshotterMinimumPixelSize = 64; dispatch_async(workQueue, ^{ // Call a class method to ensure we're not accidentally capturing self - MGLImage *compositedImage = [MGLMapSnapshotter drawAttributedSnapshotWorker:attributions snapshotImage:mglImage pointForFn:pointForFn latLngForFn:latLngForFn scale:scale size:size]; + MGLImage *compositedImage = [MGLMapSnapshotter drawAttributedSnapshotWorker:attributions snapshotImage:mglImage pointForFn:pointForFn latLngForFn:latLngForFn scale:scale size:size overlayHandler:overlayHandler]; // 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; + + if (!compositedImage) { + NSDictionary *userInfo = @{NSLocalizedDescriptionKey: @"Failed to generate composited snapshot."}; + NSError *error = [NSError errorWithDomain:MGLErrorDomain + code:MGLErrorCodeSnapshotFailed + userInfo:userInfo]; + + strongself.completion(nil, error); + strongself.completion = nil; + } else { + MGLMapSnapshot* snapshot = [[MGLMapSnapshot alloc] initWithImage:compositedImage + scale:scale + pointForFn:pointForFn + latLngForFn:latLngForFn]; + strongself.completion(snapshot, nil); + strongself.completion = nil; + } } }); }); diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index 15f5904b5d..9c8ae88e79 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -2,6 +2,9 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONTRIBUTING.md](../../CONTRIBUTING.md) to get started. +## master +* Added an `-[MGLMapSnapshotter startWithOverlayHandler:completionHandler:]` method to provide the snapshot's current `CGContext` in order to perform custom drawing on `MGLMapSnapShot` objects. ([#15530](https://github.com/mapbox/mapbox-gl-native/pull/15530)) + ## 5.4.0 ### Styles and rendering diff --git a/platform/ios/Integration Tests/Snapshotter Tests/MGLMapSnapshotterTest.m b/platform/ios/Integration Tests/Snapshotter Tests/MGLMapSnapshotterTest.m index 32e5fc782d..29014035a8 100644 --- a/platform/ios/Integration Tests/Snapshotter Tests/MGLMapSnapshotterTest.m +++ b/platform/ios/Integration Tests/Snapshotter Tests/MGLMapSnapshotterTest.m @@ -429,5 +429,62 @@ MGLMapSnapshotter* snapshotterWithCoordinates(CLLocationCoordinate2D coordinates [self waitForExpectations:@[expectation] timeout:10.0]; } +- (void)testSnapshotWithOverlayHandlerFailure { + if (![self validAccessToken]) { + return; + } + + CGSize size = self.mapView.bounds.size; + + XCTestExpectation *expectation = [self expectationWithDescription:@"snapshot with overlay fails"]; + expectation.expectedFulfillmentCount = 2; + + CLLocationCoordinate2D coord = CLLocationCoordinate2DMake(30.0, 30.0); + + MGLMapSnapshotter *snapshotter = snapshotterWithCoordinates(coord, size); + XCTAssertNotNil(snapshotter); + + [snapshotter startWithOverlayHandler:^(MGLMapSnapshotOverlay * _Nullable snapshotOverlay) { + UIGraphicsEndImageContext(); + [expectation fulfill]; + } completionHandler:^(MGLMapSnapshot * _Nullable snapshot, NSError * _Nullable error) { + XCTAssertNil(snapshot); + XCTAssertNotNil(error); + [expectation fulfill]; + }]; + + [self waitForExpectations:@[expectation] timeout:10.0]; +} + +- (void)testSnapshotWithOverlayHandlerSuccess { + if (![self validAccessToken]) { + return; + } + + CGSize size = self.mapView.bounds.size; + CGRect snapshotRect = CGRectMake(0, 0, size.width, size.height); + + XCTestExpectation *expectation = [self expectationWithDescription:@"snapshot with overlay succeeds"]; + expectation.expectedFulfillmentCount = 2; + + CLLocationCoordinate2D coord = CLLocationCoordinate2DMake(30.0, 30.0); + + MGLMapSnapshotter *snapshotter = snapshotterWithCoordinates(coord, size); + XCTAssertNotNil(snapshotter); + + [snapshotter startWithOverlayHandler:^(MGLMapSnapshotOverlay * _Nullable snapshotOverlay) { + CGContextSetFillColorWithColor(snapshotOverlay.context, [UIColor.greenColor CGColor]); + CGContextSetAlpha(snapshotOverlay.context, 0.2); + CGContextAddRect(snapshotOverlay.context, snapshotRect); + CGContextFillRect(snapshotOverlay.context, snapshotRect); + [expectation fulfill]; + } completionHandler:^(MGLMapSnapshot * _Nullable snapshot, NSError * _Nullable error) { + XCTAssertNil(error); + XCTAssertNotNil(snapshot); + [expectation fulfill]; + }]; + + [self waitForExpectations:@[expectation] timeout:10.0]; +} @end diff --git a/platform/ios/MBXContextSnapshotViewController.m b/platform/ios/MBXContextSnapshotViewController.m new file mode 100644 index 0000000000..61504f94a5 --- /dev/null +++ b/platform/ios/MBXContextSnapshotViewController.m @@ -0,0 +1,77 @@ +#import "MBXContextSnapshotViewController.h" +#import + +@interface MBXContextSnapshotViewController () +@property MGLMapView *mapView; +@property UIButton *button; +@property UIImageView *imageView; +@end + +@implementation MBXContextSnapshotViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + + _mapView = [[MGLMapView alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.height / 2) styleURL:[MGLStyle lightStyleURL]]; + _mapView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + + + [_mapView setCenterCoordinate:_mapView.centerCoordinate zoomLevel:1 animated:NO]; + [self.view addSubview:_mapView]; + + + _button = [[UIButton alloc] initWithFrame:CGRectMake(_mapView.frame.origin.x + 100, _mapView.frame.origin.y + 45, 170, 30)]; + _button.layer.cornerRadius = 15; + _button.backgroundColor = [UIColor blueColor]; + [_button setTitle:@"Create snapshot" forState:UIControlStateNormal]; + [_button addTarget:self action:@selector(createSnapshot) forControlEvents:UIControlEventTouchUpInside]; + [self.view addSubview:_button]; + + _imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, self.view.bounds.size.height / 2, self.view.bounds.size.width, self.view.bounds.size.height / 2)]; + _imageView.backgroundColor = [UIColor blackColor]; + _imageView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + [self.view addSubview:_imageView]; +} + +- (void)createSnapshot { + + MGLMapSnapshotOptions *options = [[MGLMapSnapshotOptions alloc] initWithStyleURL:_mapView.styleURL camera:_mapView.camera size:_mapView.bounds.size]; + + options.zoomLevel = _mapView.zoomLevel; + + UIActivityIndicatorView *indicator = [[UIActivityIndicatorView alloc] initWithFrame:CGRectMake(_imageView.center.x - 30, _imageView.center.y - 30, 60, 60)]; + [self.view addSubview:indicator]; + [indicator startAnimating]; + + __block MGLMapSnapshotter *snapshotter = [[MGLMapSnapshotter alloc] initWithOptions:options]; + +// [snapshotter startWithCompletionHandler:^(MGLMapSnapshot * _Nullable snapshot, NSError * _Nullable error) { +// if (error != nil) { +// NSLog(@"Unable to create a map snapshot."); +// } else if (snapshot != nil) { +// // Add the map snapshot's image to the image view. +// [indicator stopAnimating]; +// self.imageView.image = snapshot.image; +// } +// +// snapshotter = nil; +// }]; + + [snapshotter startWithOverlayHandler:^(NSString * _Nullable testString) { + NSLog(@"STRING: %@", testString); + } completionHandler:^(MGLMapSnapshot * _Nullable snapshot, NSError * _Nullable error) { + if (error != nil) { + NSLog(@"Unable to create a map snapshot."); + } else if (snapshot != nil) { + // Add the map snapshot's image to the image view. + [indicator stopAnimating]; + self.imageView.image = snapshot.image; + } + + snapshotter = nil; + }]; + + +} + +@end diff --git a/platform/ios/app/MBXContextSnapshotViewController.h b/platform/ios/app/MBXContextSnapshotViewController.h new file mode 100644 index 0000000000..69793e5c7e --- /dev/null +++ b/platform/ios/app/MBXContextSnapshotViewController.h @@ -0,0 +1,17 @@ +// +// MBXContextSnapshotViewController.h +// iosapp +// +// Created by Nadia Barbosa on 9/9/19. +// Copyright © 2019 Mapbox. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface MBXContextSnapshotViewController : UIViewController + +@end + +NS_ASSUME_NONNULL_END diff --git a/platform/ios/app/MBXContextSnapshotViewController.m b/platform/ios/app/MBXContextSnapshotViewController.m new file mode 100644 index 0000000000..09640ca95b --- /dev/null +++ b/platform/ios/app/MBXContextSnapshotViewController.m @@ -0,0 +1,92 @@ +#import "MBXContextSnapshotViewController.h" +#import + +@interface MBXContextSnapshotViewController () +@property MGLMapView *mapView; +@property UIButton *button; +@property UIImageView *imageView; +@end + +@implementation MBXContextSnapshotViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + + _mapView = [[MGLMapView alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.height / 2) styleURL:[MGLStyle lightStyleURL]]; + _mapView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + + + [_mapView setCenterCoordinate:_mapView.centerCoordinate zoomLevel:1 animated:NO]; + [self.view addSubview:_mapView]; + + _button = [[UIButton alloc] initWithFrame:CGRectMake(_mapView.frame.origin.x + 100, _mapView.frame.origin.y + 45, 170, 30)]; + _button.layer.cornerRadius = 15; + _button.backgroundColor = [UIColor blueColor]; + [_button setTitle:@"Create snapshot" forState:UIControlStateNormal]; + [_button addTarget:self action:@selector(createSnapshot) forControlEvents:UIControlEventTouchUpInside]; + [self.view addSubview:_button]; + + _imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, self.view.bounds.size.height / 2, self.view.bounds.size.width, self.view.bounds.size.height / 2)]; + _imageView.backgroundColor = [UIColor blackColor]; + _imageView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + [self.view addSubview:_imageView]; +} + +- (void)createSnapshot { + + MGLMapSnapshotOptions *options = [[MGLMapSnapshotOptions alloc] initWithStyleURL:_mapView.styleURL camera:_mapView.camera size:_mapView.bounds.size]; + + options.zoomLevel = _mapView.zoomLevel; + + UIActivityIndicatorView *indicator = [[UIActivityIndicatorView alloc] initWithFrame:CGRectMake(_imageView.center.x - 30, _imageView.center.y - 30, 60, 60)]; + [self.view addSubview:indicator]; + [indicator startAnimating]; + + CGRect imageRect = _imageView.bounds; + + __block MGLMapSnapshotter *snapshotter = [[MGLMapSnapshotter alloc] initWithOptions:options]; + + [snapshotter startWithOverlayHandler:^(MGLMapSnapshotOverlay * _Nullable snapshotOverlay) { + // Overlays red square over snapshot (but below attribution) +// CGContextSetFillColorWithColor(snapshotOverlay.context, [UIColor.greenColor CGColor]); +// CGContextSetAlpha(snapshotOverlay.context, 0.2); +// CGContextAddRect(snapshotOverlay.context, imageRect); +// CGContextFillRect(snapshotOverlay.context, imageRect); + + // Slick drawing, but pushes attribution out of the way + CGContextSetStrokeColorWithColor(snapshotOverlay.context, [UIColor.redColor CGColor]); + CGContextTranslateCTM(snapshotOverlay.context, imageRect.size.width / 2, imageRect.size.height / 2); + BOOL first = YES; + CGFloat length = 256; + + for (int i = 1; i <= length; i++) + { + CGContextRotateCTM(snapshotOverlay.context, M_PI / 2); + + if (first) { + CGContextMoveToPoint(snapshotOverlay.context, length, 50); + first = NO; + } else { + CGContextAddLineToPoint(snapshotOverlay.context, length, 50); + } + + length *= 0.99; + } + + CGContextStrokePath(snapshotOverlay.context); + + } completionHandler:^(MGLMapSnapshot * _Nullable snapshot, NSError * _Nullable error) { + if (error != nil) { + NSLog(@"Unable to create a map snapshot."); + } else if (snapshot != nil) { + // Add the map snapshot's image to the image view. + [indicator stopAnimating]; + self.imageView.image = snapshot.image; + } + + snapshotter = nil; + }]; +} + +@end + diff --git a/platform/ios/app/Main.storyboard b/platform/ios/app/Main.storyboard index ac83bd968f..04cda22eff 100644 --- a/platform/ios/app/Main.storyboard +++ b/platform/ios/app/Main.storyboard @@ -1,9 +1,9 @@ - + - + @@ -13,11 +13,11 @@ - + - + + + + + + + + + + + + + + + + diff --git a/platform/ios/ios.xcodeproj/project.pbxproj b/platform/ios/ios.xcodeproj/project.pbxproj index 7712c332c4..bf575c44a8 100644 --- a/platform/ios/ios.xcodeproj/project.pbxproj +++ b/platform/ios/ios.xcodeproj/project.pbxproj @@ -12,6 +12,7 @@ 071BBB031EE76146001FB02A /* MGLImageSource.h in Headers */ = {isa = PBXBuildFile; fileRef = 071BBAFC1EE75CD4001FB02A /* MGLImageSource.h */; settings = {ATTRIBUTES = (Public, ); }; }; 071BBB041EE76147001FB02A /* MGLImageSource.h in Headers */ = {isa = PBXBuildFile; fileRef = 071BBAFC1EE75CD4001FB02A /* MGLImageSource.h */; settings = {ATTRIBUTES = (Public, ); }; }; 071BBB071EE77631001FB02A /* MGLImageSourceTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 071BBB051EE7761A001FB02A /* MGLImageSourceTests.m */; }; + 0723AB85232717AB0095FD55 /* MBXContextSnapshotViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 0723AB84232717AB0095FD55 /* MBXContextSnapshotViewController.m */; }; 075AF842227B6762008D7A4C /* MBXState.m in Sources */ = {isa = PBXBuildFile; fileRef = 075AF841227B6762008D7A4C /* MBXState.m */; }; 075AF845227B67C6008D7A4C /* MBXStateManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 075AF844227B67C6008D7A4C /* MBXStateManager.m */; }; 076171C32139C70900668A35 /* MGLMapViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 076171C22139C70900668A35 /* MGLMapViewTests.m */; }; @@ -868,6 +869,8 @@ 071BBAFC1EE75CD4001FB02A /* MGLImageSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLImageSource.h; sourceTree = ""; }; 071BBAFD1EE75CD4001FB02A /* MGLImageSource.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLImageSource.mm; sourceTree = ""; }; 071BBB051EE7761A001FB02A /* MGLImageSourceTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGLImageSourceTests.m; path = ../../darwin/test/MGLImageSourceTests.m; sourceTree = ""; }; + 0723AB83232717AB0095FD55 /* MBXContextSnapshotViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MBXContextSnapshotViewController.h; sourceTree = ""; }; + 0723AB84232717AB0095FD55 /* MBXContextSnapshotViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MBXContextSnapshotViewController.m; sourceTree = ""; }; 074A7F0C2277C093001A62D1 /* insert_access_token.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = insert_access_token.sh; sourceTree = ""; }; 075AF840227B6762008D7A4C /* MBXState.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MBXState.h; sourceTree = ""; }; 075AF841227B6762008D7A4C /* MBXState.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MBXState.m; sourceTree = ""; }; @@ -1983,6 +1986,8 @@ DA1DC94C1CB6C1C2006E619F /* Demo App */ = { isa = PBXGroup; children = ( + 0723AB83232717AB0095FD55 /* MBXContextSnapshotViewController.h */, + 0723AB84232717AB0095FD55 /* MBXContextSnapshotViewController.m */, 3E6465D52065767A00685536 /* LimeGreenStyleLayer.h */, 3E6465D42065767A00685536 /* LimeGreenStyleLayer.m */, DA1DC9501CB6C1C2006E619F /* MBXAppDelegate.h */, @@ -3248,6 +3253,7 @@ DA1DC96B1CB6C6B7006E619F /* MBXOfflinePacksTableViewController.m in Sources */, 075AF845227B67C6008D7A4C /* MBXStateManager.m in Sources */, DA1DC96A1CB6C6B7006E619F /* MBXCustomCalloutView.m in Sources */, + 0723AB85232717AB0095FD55 /* MBXContextSnapshotViewController.m in Sources */, 075AF842227B6762008D7A4C /* MBXState.m in Sources */, 927FBCFC1F4DAA8300F8BF1F /* MBXSnapshotsViewController.m in Sources */, DA1DC99B1CB6E064006E619F /* MBXViewController.m in Sources */, diff --git a/platform/macos/CHANGELOG.md b/platform/macos/CHANGELOG.md index cbf08f7dd3..da9a179ad3 100644 --- a/platform/macos/CHANGELOG.md +++ b/platform/macos/CHANGELOG.md @@ -17,6 +17,7 @@ * Fixed a rendering issue that non-SDF icon would be treated as SDF icon if they are in the same layer. ([#15456](https://github.com/mapbox/mapbox-gl-native/pull/15456)) * Fixed a rendering issue of `collisionBox` when `text-translate` or `icon-translate` is enabled. ([#15467](https://github.com/mapbox/mapbox-gl-native/pull/15467)) * Fixed an issue of integer overflow when converting `tileCoordinates` to `LatLon`, which caused issues such as `queryRenderedFeatures` and `querySourceFeatures` returning incorrect coordinates at zoom levels 20 and higher. ([#15560](https://github.com/mapbox/mapbox-gl-native/pull/15560)) +* Added an `-[MGLMapSnapshotter startWithOverlayHandler:completionHandler:]` method to provide the snapshot's current `CGContext` in order to perform custom drawing on `MGLMapSnapShot` objects. ([#15530](https://github.com/mapbox/mapbox-gl-native/pull/15530)) ### Styles and rendering -- cgit v1.2.1