792c7de9b8ddad6d59fbe0ff9d8334aa4574dd02
[WebKit-https.git] / Source / WebKit / Shared / ios / WebIconUtilities.mm
1 /*
2  * Copyright (C) 2017 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "WebIconUtilities.h"
28
29 #if PLATFORM(IOS_FAMILY)
30
31 #import "UIKitSPI.h"
32 #import <AVFoundation/AVFoundation.h>
33 #import <CoreGraphics/CoreGraphics.h>
34 #import <CoreMedia/CoreMedia.h>
35 #import <ImageIO/ImageIO.h>
36 #import <MobileCoreServices/MobileCoreServices.h>
37 #import <wtf/MathExtras.h>
38 #import <wtf/RetainPtr.h>
39
40 #import <pal/cf/CoreMediaSoftLink.h>
41 #import <pal/cocoa/AVFoundationSoftLink.h>
42
43 namespace WebKit {
44
45 static const CGFloat iconSideLength = 100;
46
47 static CGRect squareCropRectForSize(CGSize size)
48 {
49     CGFloat smallerSide = std::min(size.width, size.height);
50     CGRect cropRect = CGRectMake(0, 0, smallerSide, smallerSide);
51
52     if (size.width < size.height)
53         cropRect.origin.y = std::round((size.height - smallerSide) / 2);
54     else
55         cropRect.origin.x = std::round((size.width - smallerSide) / 2);
56
57     return cropRect;
58 }
59
60 static UIImage *squareImage(CGImageRef image)
61 {
62     if (!image)
63         return nil;
64
65     CGSize imageSize = CGSizeMake(CGImageGetWidth(image), CGImageGetHeight(image));
66     if (imageSize.width == imageSize.height)
67         return [UIImage imageWithCGImage:image];
68
69     CGRect squareCropRect = squareCropRectForSize(imageSize);
70     RetainPtr<CGImageRef> squareImage = adoptCF(CGImageCreateWithImageInRect(image, squareCropRect));
71     return [UIImage imageWithCGImage:squareImage.get()];
72 }
73
74 static UIImage *thumbnailSizedImageForImage(CGImageRef image)
75 {
76     UIImage *squaredImage = squareImage(image);
77     if (!squaredImage)
78         return nil;
79
80     CGRect destRect = CGRectMake(0, 0, iconSideLength, iconSideLength);
81     UIGraphicsBeginImageContext(destRect.size);
82     CGContextSetInterpolationQuality(UIGraphicsGetCurrentContext(), kCGInterpolationHigh);
83     [squaredImage drawInRect:destRect];
84     UIImage *resultImage = UIGraphicsGetImageFromCurrentImageContext();
85     UIGraphicsEndImageContext();
86     return resultImage;
87 }
88
89 UIImage* fallbackIconForFile(NSURL *file)
90 {
91     ASSERT_ARG(file, [file isFileURL]);
92
93     UIDocumentInteractionController *interactionController = [UIDocumentInteractionController interactionControllerWithURL:file];
94     return thumbnailSizedImageForImage(interactionController.icons[0].CGImage);
95 }
96
97 UIImage* iconForImageFile(NSURL *file)
98 {
99     ASSERT_ARG(file, [file isFileURL]);
100
101     NSDictionary *options = @{
102         (id)kCGImageSourceCreateThumbnailFromImageIfAbsent: @YES,
103         (id)kCGImageSourceThumbnailMaxPixelSize: @(iconSideLength),
104         (id)kCGImageSourceCreateThumbnailWithTransform: @YES,
105     };
106     RetainPtr<CGImageSource> imageSource = adoptCF(CGImageSourceCreateWithURL((CFURLRef)file, 0));
107     RetainPtr<CGImageRef> thumbnail = adoptCF(CGImageSourceCreateThumbnailAtIndex(imageSource.get(), 0, (CFDictionaryRef)options));
108     if (!thumbnail) {
109         LOG_ERROR("Error creating thumbnail image for image: %@", file);
110         return fallbackIconForFile(file);
111     }
112
113     return thumbnailSizedImageForImage(thumbnail.get());
114 }
115
116 UIImage* iconForVideoFile(NSURL *file)
117 {
118     ASSERT_ARG(file, [file isFileURL]);
119
120     RetainPtr<AVURLAsset> asset = adoptNS([PAL::allocAVURLAssetInstance() initWithURL:file options:nil]);
121     RetainPtr<AVAssetImageGenerator> generator = adoptNS([PAL::allocAVAssetImageGeneratorInstance() initWithAsset:asset.get()]);
122     [generator setAppliesPreferredTrackTransform:YES];
123
124     NSError *error = nil;
125     RetainPtr<CGImageRef> imageRef = adoptCF([generator copyCGImageAtTime:kCMTimeZero actualTime:nil error:&error]);
126     if (!imageRef) {
127         LOG_ERROR("Error creating image for video '%@': %@", file, error);
128         return fallbackIconForFile(file);
129     }
130
131     return thumbnailSizedImageForImage(imageRef.get());
132 }
133
134 UIImage* iconForFile(NSURL *file)
135 {
136     ASSERT_ARG(file, [file isFileURL]);
137
138     NSString *fileExtension = file.pathExtension;
139     if (!fileExtension.length)
140         return nil;
141
142     RetainPtr<CFStringRef> fileUTI = adoptCF(UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (CFStringRef)fileExtension, 0));
143
144     if (UTTypeConformsTo(fileUTI.get(), kUTTypeImage))
145         return iconForImageFile(file);
146
147     if (UTTypeConformsTo(fileUTI.get(), kUTTypeMovie))
148         return iconForVideoFile(file);
149
150     return fallbackIconForFile(file);
151 }
152
153 }
154
155 #endif