/* * Copyright (C) 2017 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #import "config.h" #import "WebIconUtilities.h" #if PLATFORM(IOS_FAMILY) #import "UIKitSPI.h" #import #import #import #import #import #import #import #import #import namespace WebKit { static const CGFloat iconSideLength = 100; static CGRect squareCropRectForSize(CGSize size) { CGFloat smallerSide = std::min(size.width, size.height); CGRect cropRect = CGRectMake(0, 0, smallerSide, smallerSide); if (size.width < size.height) cropRect.origin.y = std::round((size.height - smallerSide) / 2); else cropRect.origin.x = std::round((size.width - smallerSide) / 2); return cropRect; } static UIImage *squareImage(CGImageRef image) { if (!image) return nil; CGSize imageSize = CGSizeMake(CGImageGetWidth(image), CGImageGetHeight(image)); if (imageSize.width == imageSize.height) return [UIImage imageWithCGImage:image]; CGRect squareCropRect = squareCropRectForSize(imageSize); RetainPtr squareImage = adoptCF(CGImageCreateWithImageInRect(image, squareCropRect)); return [UIImage imageWithCGImage:squareImage.get()]; } static RetainPtr thumbnailSizedImageForImage(CGImageRef image) { UIImage *squaredImage = squareImage(image); if (!squaredImage) return nil; CGRect destRect = CGRectMake(0, 0, iconSideLength, iconSideLength); UIGraphicsBeginImageContext(destRect.size); CGContextSetInterpolationQuality(UIGraphicsGetCurrentContext(), kCGInterpolationHigh); [squaredImage drawInRect:destRect]; RetainPtr resultImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return resultImage; } RetainPtr fallbackIconForFile(NSURL *file) { ASSERT_ARG(file, [file isFileURL]); UIDocumentInteractionController *interactionController = [UIDocumentInteractionController interactionControllerWithURL:file]; if (![interactionController.icons count]) return nil; return thumbnailSizedImageForImage(interactionController.icons[0].CGImage); } RetainPtr iconForImageFile(NSURL *file) { ASSERT_ARG(file, [file isFileURL]); NSDictionary *options = @{ (id)kCGImageSourceCreateThumbnailFromImageIfAbsent: @YES, (id)kCGImageSourceThumbnailMaxPixelSize: @(iconSideLength), (id)kCGImageSourceCreateThumbnailWithTransform: @YES, }; RetainPtr imageSource = adoptCF(CGImageSourceCreateWithURL((CFURLRef)file, 0)); RetainPtr thumbnail = adoptCF(CGImageSourceCreateThumbnailAtIndex(imageSource.get(), 0, (CFDictionaryRef)options)); if (!thumbnail) { LOG_ERROR("Error creating thumbnail image for image: %@", file); return fallbackIconForFile(file); } return thumbnailSizedImageForImage(thumbnail.get()); } RetainPtr iconForVideoFile(NSURL *file) { ASSERT_ARG(file, [file isFileURL]); RetainPtr asset = adoptNS([PAL::allocAVURLAssetInstance() initWithURL:file options:nil]); RetainPtr generator = adoptNS([PAL::allocAVAssetImageGeneratorInstance() initWithAsset:asset.get()]); [generator setAppliesPreferredTrackTransform:YES]; NSError *error = nil; RetainPtr imageRef = adoptCF([generator copyCGImageAtTime:PAL::kCMTimeZero actualTime:nil error:&error]); if (!imageRef) { LOG_ERROR("Error creating image for video '%@': %@", file, error); return fallbackIconForFile(file); } return thumbnailSizedImageForImage(imageRef.get()); } RetainPtr iconForFile(NSURL *file) { ASSERT_ARG(file, [file isFileURL]); NSString *fileExtension = file.pathExtension; if (!fileExtension.length) return nil; ALLOW_DEPRECATED_DECLARATIONS_BEGIN RetainPtr fileUTI = adoptCF(UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (CFStringRef)fileExtension, 0)); if (UTTypeConformsTo(fileUTI.get(), kUTTypeImage)) return iconForImageFile(file); if (UTTypeConformsTo(fileUTI.get(), kUTTypeMovie)) return iconForVideoFile(file); ALLOW_DEPRECATED_DECLARATIONS_END return fallbackIconForFile(file); } } #endif