From 93e363bd8c98dbdc9dca9e4be77bbee6215dc9c7 Mon Sep 17 00:00:00 2001 From: Fabian Guerra Date: Wed, 1 Nov 2017 19:37:31 -0400 Subject: [ios, macos] Add attribution to snapshots. --- platform/darwin/src/MGLMapSnapshotter.mm | 119 ++++++++++++++++++++++++++++++- 1 file changed, 117 insertions(+), 2 deletions(-) diff --git a/platform/darwin/src/MGLMapSnapshotter.mm b/platform/darwin/src/MGLMapSnapshotter.mm index a00521a87a..be76a30511 100644 --- a/platform/darwin/src/MGLMapSnapshotter.mm +++ b/platform/darwin/src/MGLMapSnapshotter.mm @@ -14,11 +14,14 @@ #import "MGLGeometry_Private.h" #import "NSBundle+MGLAdditions.h" #import "MGLStyle.h" +#import "MGLAttributionInfo_Private.h" #if TARGET_OS_IPHONE #import "UIImage+MGLAdditions.h" #else #import "NSImage+MGLAdditions.h" +#import +#import #endif const CGPoint MGLLogoImagePosition = CGPointMake(8, 8); @@ -146,6 +149,33 @@ const CGFloat MGLSnapshotterMinimumPixelSize = 64; dispatch_async(queue, ^{ _snapshotCallback = std::make_unique>(*mbgl::Scheduler::GetCurrent(), [=](std::exception_ptr mbglError, mbgl::PremultipliedImage image, mbgl::MapSnapshotter::Attributions attributions, mbgl::MapSnapshotter::PointForFn pointForFn) { _loading = false; + + NSMutableArray *infos = [NSMutableArray array]; + +#if TARGET_OS_IPHONE + CGFloat fontSize = [UIFont smallSystemFontSize]; + UIColor *attributeFontColor = [UIColor blackColor]; +#else + CGFloat fontSize = [NSFont systemFontSizeForControlSize:NSMiniControlSize]; + NSColor *attributeFontColor = [NSColor blackColor]; +#endif + for (auto attribution = attributions.begin(); attribution != attributions.end(); ++attribution) { + NSString *attributionHTMLString = @(attribution->c_str()); + NSArray *tileSetInfos = [MGLAttributionInfo attributionInfosFromHTMLString:attributionHTMLString + fontSize:fontSize + linkColor:attributeFontColor]; + [infos growArrayByAddingAttributionInfosFromArray:tileSetInfos]; + } + + CGSize attributionBackgroundSize = CGSizeMake(10, 0); + for (MGLAttributionInfo *info in infos) { + if (info.isFeedbackLink) { + continue; + } + attributionBackgroundSize.width += [info.title size].width + 10; + attributionBackgroundSize.height = MAX([info.title size].height, attributionBackgroundSize.height); + } + if (mbglError) { NSString *description = @(mbgl::util::toString(mbglError).c_str()); NSDictionary *userInfo = @{NSLocalizedDescriptionKey: description}; @@ -170,10 +200,33 @@ const CGFloat MGLSnapshotterMinimumPixelSize = 64; #if TARGET_OS_IPHONE UIImage *logoImage = [UIImage imageNamed:@"mapbox" inBundle:[NSBundle mgl_frameworkBundle] compatibleWithTraitCollection:nil]; + CGRect logoImageRect = CGRectMake(MGLLogoImagePosition.x, mglImage.size.height - (MGLLogoImagePosition.y + logoImage.size.height), logoImage.size.width, logoImage.size.height); + + CGPoint attributionTextPosition = CGPointMake(mglImage.size.width - 10 - attributionBackgroundSize.width, + logoImageRect.origin.y + (logoImageRect.size.height / 2) - (attributionBackgroundSize.height / 2)); + + CGRect cropRect = CGRectMake(attributionTextPosition.x * mglImage.scale, (attributionTextPosition.y + 1) * mglImage.scale, attributionBackgroundSize.width * mglImage.scale, attributionBackgroundSize.height * mglImage.scale); + + UIGraphicsBeginImageContextWithOptions(mglImage.size, NO, self.options.scale); [mglImage drawInRect:CGRectMake(0, 0, mglImage.size.width, mglImage.size.height)]; - [logoImage drawInRect:CGRectMake(MGLLogoImagePosition.x, mglImage.size.height - (MGLLogoImagePosition.y + logoImage.size.height), logoImage.size.width,logoImage.size.height)]; + + [logoImage drawInRect:logoImageRect]; + + UIImage *currentImage = UIGraphicsGetImageFromCurrentImageContext(); + CGImageRef attributionImageRef = CGImageCreateWithImageInRect([currentImage CGImage], cropRect); + UIImage *attributionImage = [UIImage imageWithCGImage:attributionImageRef]; + CGImageRelease(attributionImageRef); + + CIImage *ciAttributionImage = [[CIImage alloc] initWithCGImage:attributionImage.CGImage]; + + UIImage *blurredAttributionBackground = [self blurredAttributionBackground:ciAttributionImage]; + + [blurredAttributionBackground drawInRect:CGRectMake(attributionTextPosition.x, attributionTextPosition.y + 1, attributionBackgroundSize.width, attributionBackgroundSize.height)]; + + [self drawAttributionText:infos origin:CGPointMake(attributionTextPosition.x + 10, attributionTextPosition.y)]; + UIImage *compositedImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); @@ -183,6 +236,11 @@ const CGFloat MGLSnapshotterMinimumPixelSize = 64; NSSize targetSize = NSMakeSize(self.options.size.width, self.options.size.height); NSRect targetFrame = NSMakeRect(0, 0, targetSize.width, targetSize.height); + CGRect logoImageRect = CGRectMake(MGLLogoImagePosition.x, MGLLogoImagePosition.y, logoImage.size.width, logoImage.size.height); + CGPoint attributionTextPosition = CGPointMake(targetFrame.size.width - 10 - attributionBackgroundSize.width, + logoImageRect.origin.y + (logoImageRect.size.height / 2) - (attributionBackgroundSize.height / 2)); + + NSImage *compositedImage = nil; NSImageRep *sourceImageRep = [sourceImage bestRepresentationForRect:targetFrame context:nil @@ -190,10 +248,26 @@ const CGFloat MGLSnapshotterMinimumPixelSize = 64; compositedImage = [[NSImage alloc] initWithSize:targetSize]; [compositedImage lockFocus]; + [sourceImageRep drawInRect: targetFrame]; - [logoImage drawInRect:CGRectMake(MGLLogoImagePosition.x, MGLLogoImagePosition.y, logoImage.size.width,logoImage.size.height)]; + + [logoImage drawInRect:logoImageRect]; + + NSRect cropRect = NSMakeRect(attributionTextPosition.x, MGLLogoImagePosition.y + 1, attributionBackgroundSize.width, attributionBackgroundSize.height); + + NSBitmapImageRep *attributionBackground = [[NSBitmapImageRep alloc] initWithFocusedViewRect: cropRect]; + + CIImage *attributionBackgroundImage = [[CIImage alloc] initWithCGImage:[attributionBackground CGImage]]; + + NSImage *blurredAttributionBackground = [self blurredAttributionBackground:attributionBackgroundImage]; + + [blurredAttributionBackground drawInRect:CGRectMake(attributionTextPosition.x, MGLLogoImagePosition.y + 1, attributionBackgroundSize.width, attributionBackgroundSize.height)]; + + [self drawAttributionText:infos origin:CGPointMake(attributionTextPosition.x + 10, attributionTextPosition.y)]; + [compositedImage unlockFocus]; + #endif // Dispatch result to origin queue @@ -208,6 +282,47 @@ const CGFloat MGLSnapshotterMinimumPixelSize = 64; }); } +- (void)drawAttributionText:(NSArray *)attributionInfo origin:(CGPoint)origin +{ + for (MGLAttributionInfo *info in attributionInfo) { + if (info.isFeedbackLink) { + continue; + } + [info.title drawAtPoint:origin]; + + origin.x += [info.title size].width + 10; + } +} + +- (MGLImage *)blurredAttributionBackground:(CIImage *)backgroundImage +{ + CGAffineTransform transform = CGAffineTransformIdentity; + CIFilter *clamp = [CIFilter filterWithName:@"CIAffineClamp"]; + [clamp setValue:backgroundImage forKey:kCIInputImageKey]; + [clamp setValue:[NSValue valueWithBytes:&transform objCType:@encode(CGAffineTransform)] forKey:@"inputTransform"]; + + CIFilter *attributionBlurFilter = [CIFilter filterWithName:@"CIGaussianBlur"]; + [attributionBlurFilter setValue:[clamp outputImage] forKey:kCIInputImageKey]; + [attributionBlurFilter setValue:@10 forKey:kCIInputRadiusKey]; + + CIFilter *attributionColorFilter = [CIFilter filterWithName:@"CIColorControls"]; + [attributionColorFilter setValue:[attributionBlurFilter outputImage] forKey:kCIInputImageKey]; + [attributionColorFilter setValue:@(0.1) forKey:kCIInputBrightnessKey]; + + CIImage *blurredImage = attributionColorFilter.outputImage; + + CIContext *ctx = [CIContext contextWithOptions:nil]; + CGImageRef cgimg = [ctx createCGImage:blurredImage fromRect:[backgroundImage extent]]; + +#if TARGET_OS_IPHONE + + return [UIImage imageWithCGImage:cgimg]; +#else + + return [[NSImage alloc] initWithCGImage:cgimg size:[backgroundImage extent].size]; +#endif +} + - (void)cancel { _snapshotCallback.reset(); -- cgit v1.2.1