Title: [185357] trunk/Source/WebKit2
Revision
185357
Author
[email protected]
Date
2015-06-09 01:18:31 -0700 (Tue, 09 Jun 2015)

Log Message

[iOS] UI process memory spike, hang when uploading a very large JPEG

<https://bugs.webkit.org/show_bug.cgi?id=145567>
<rdar://problem/21206699>

When uploading an image, we generate a thumbnail to use for an icon
that sits next to the control. To do this, we load the image and draw
it into a thumbnail-sized canvas. This can be very expensive for large
images.

To fix this, we'll use CGImageSource API, which will allow us to get
any existing thumbnail that exists in the source file.

Reviewed by Darin Adler.

* UIProcess/ios/forms/WKFileUploadPanel.mm:
(squareCropRectForSize):
Use std::round, based on Darin's review feedback that a) rintf is
incorrect in 64-bit systems where CGFloat is a double, b) rintf uses a
variable rounding mode, which is undesirable.
(squareImage):
Changed to take a CGImageRef.
Use RetainPtr and +[UIImage imageWithCGImage:] to match other code in
this file.
(thumbnailSizedImageForImage):
Changed to take a CGImageRef and to use iconSideLength.
(iconForImageFile):
Create an image source for the file, then use CGImageSource API to
generate a thumbnail.
(-[_WKImageFileUploadItem initWithFileURL:originalImage:]): Deleted.
(-[_WKImageFileUploadItem displayImage]):
Use iconForImageFile() rather than trying to generate an icon from the
UIImage.
(-[WKFileUploadPanel _uploadItemForImageData:imageName:successBlock:failureBlock:]):
Removed originalImage parameter; _WKImageFileUploadItem no longer uses
it.
(-[WKFileUploadPanel _uploadItemForJPEGRepresentationOfImage:successBlock:failureBlock:]):
(-[WKFileUploadPanel _uploadItemForImage:withAssetURL:successBlock:failureBlock:]):

Modified Paths

Diff

Modified: trunk/Source/WebKit2/ChangeLog (185356 => 185357)


--- trunk/Source/WebKit2/ChangeLog	2015-06-09 07:45:12 UTC (rev 185356)
+++ trunk/Source/WebKit2/ChangeLog	2015-06-09 08:18:31 UTC (rev 185357)
@@ -1,3 +1,44 @@
+2015-06-02  Jon Honeycutt  <[email protected]>
+
+        [iOS] UI process memory spike, hang when uploading a very large JPEG
+
+        <https://bugs.webkit.org/show_bug.cgi?id=145567>
+        <rdar://problem/21206699>
+
+        When uploading an image, we generate a thumbnail to use for an icon
+        that sits next to the control. To do this, we load the image and draw
+        it into a thumbnail-sized canvas. This can be very expensive for large
+        images.
+
+        To fix this, we'll use CGImageSource API, which will allow us to get
+        any existing thumbnail that exists in the source file.
+
+        Reviewed by Darin Adler.
+
+        * UIProcess/ios/forms/WKFileUploadPanel.mm:
+        (squareCropRectForSize):
+        Use std::round, based on Darin's review feedback that a) rintf is
+        incorrect in 64-bit systems where CGFloat is a double, b) rintf uses a
+        variable rounding mode, which is undesirable.
+        (squareImage):
+        Changed to take a CGImageRef.
+        Use RetainPtr and +[UIImage imageWithCGImage:] to match other code in
+        this file.
+        (thumbnailSizedImageForImage):
+        Changed to take a CGImageRef and to use iconSideLength.
+        (iconForImageFile):
+        Create an image source for the file, then use CGImageSource API to
+        generate a thumbnail.
+        (-[_WKImageFileUploadItem initWithFileURL:originalImage:]): Deleted.
+        (-[_WKImageFileUploadItem displayImage]):
+        Use iconForImageFile() rather than trying to generate an icon from the
+        UIImage.
+        (-[WKFileUploadPanel _uploadItemForImageData:imageName:successBlock:failureBlock:]):
+        Removed originalImage parameter; _WKImageFileUploadItem no longer uses
+        it.
+        (-[WKFileUploadPanel _uploadItemForJPEGRepresentationOfImage:successBlock:failureBlock:]):
+        (-[WKFileUploadPanel _uploadItemForImage:withAssetURL:successBlock:failureBlock:]):
+
 2015-06-08  Dan Bernstein  <[email protected]>
 
         [Cocoa] Use generics in framework headers

Modified: trunk/Source/WebKit2/UIProcess/ios/forms/WKFileUploadPanel.mm (185356 => 185357)


--- trunk/Source/WebKit2/UIProcess/ios/forms/WKFileUploadPanel.mm	2015-06-09 07:45:12 UTC (rev 185356)
+++ trunk/Source/WebKit2/UIProcess/ios/forms/WKFileUploadPanel.mm	2015-06-09 08:18:31 UTC (rev 185357)
@@ -90,43 +90,42 @@
 
 #pragma mark - Icon generation
 
+static const CGFloat iconSideLength = 100;
+
 static CGRect squareCropRectForSize(CGSize size)
 {
     CGFloat smallerSide = MIN(size.width, size.height);
     CGRect cropRect = CGRectMake(0, 0, smallerSide, smallerSide);
 
     if (size.width < size.height)
-        cropRect.origin.y = rintf((size.height - smallerSide) / 2);
+        cropRect.origin.y = std::round((size.height - smallerSide) / 2);
     else
-        cropRect.origin.x = rintf((size.width - smallerSide) / 2);
+        cropRect.origin.x = std::round((size.width - smallerSide) / 2);
 
     return cropRect;
 }
 
-static UIImage *squareImage(UIImage *image)
+static UIImage *squareImage(CGImageRef image)
 {
     if (!image)
         return nil;
 
-    CGImageRef imageRef = [image CGImage];
-    CGSize imageSize = CGSizeMake(CGImageGetWidth(imageRef), CGImageGetHeight(imageRef));
+    CGSize imageSize = CGSizeMake(CGImageGetWidth(image), CGImageGetHeight(image));
     if (imageSize.width == imageSize.height)
-        return image;
+        return [UIImage imageWithCGImage:image];
 
     CGRect squareCropRect = squareCropRectForSize(imageSize);
-    CGImageRef squareImageRef = CGImageCreateWithImageInRect(imageRef, squareCropRect);
-    UIImage *squareImage = [[UIImage alloc] initWithCGImage:squareImageRef imageOrientation:[image imageOrientation]];
-    CGImageRelease(squareImageRef);
-    return [squareImage autorelease];
+    RetainPtr<CGImageRef> squareImage = adoptCF(CGImageCreateWithImageInRect(image, squareCropRect));
+    return [UIImage imageWithCGImage:squareImage.get()];
 }
 
-static UIImage *thumbnailSizedImageForImage(UIImage *image)
+static UIImage *thumbnailSizedImageForImage(CGImageRef image)
 {
     UIImage *squaredImage = squareImage(image);
     if (!squaredImage)
         return nil;
 
-    CGRect destRect = CGRectMake(0, 0, 100, 100);
+    CGRect destRect = CGRectMake(0, 0, iconSideLength, iconSideLength);
     UIGraphicsBeginImageContext(destRect.size);
     CGContextSetInterpolationQuality(UIGraphicsGetCurrentContext(), kCGInterpolationHigh);
     [squaredImage drawInRect:destRect];
@@ -140,18 +139,26 @@
     ASSERT_ARG(file, [file isFileURL]);
 
     UIDocumentInteractionController *interactionController = [UIDocumentInteractionController interactionControllerWithURL:file];
-    return thumbnailSizedImageForImage(interactionController.icons[0]);
+    return thumbnailSizedImageForImage(interactionController.icons[0].CGImage);
 }
 
 static UIImage* iconForImageFile(NSURL *file)
 {
     ASSERT_ARG(file, [file isFileURL]);
 
-    if (UIImage *image = [UIImage imageWithContentsOfFile:file.path])
-        return thumbnailSizedImageForImage(image);
+    NSDictionary *options = @{
+        (id)kCGImageSourceCreateThumbnailFromImageIfAbsent: @YES,
+        (id)kCGImageSourceThumbnailMaxPixelSize: @(iconSideLength),
+        (id)kCGImageSourceCreateThumbnailWithTransform: @YES,
+    };
+    RetainPtr<CGImageSource> imageSource = adoptCF(CGImageSourceCreateWithURL((CFURLRef)file, 0));
+    RetainPtr<CGImageRef> thumbnail = adoptCF(CGImageSourceCreateThumbnailAtIndex(imageSource.get(), 0, (CFDictionaryRef)options));
+    if (!thumbnail) {
+        LOG_ERROR("WKFileUploadPanel: Error creating thumbnail image for image: %@", file);
+        return fallbackIconForFile(file);
+    }
 
-    LOG_ERROR("WKFileUploadPanel: Error creating thumbnail image for image: %@", file);
-    return fallbackIconForFile(file);
+    return thumbnailSizedImageForImage(thumbnail.get());
 }
 
 static UIImage* iconForVideoFile(NSURL *file)
@@ -169,8 +176,7 @@
         return fallbackIconForFile(file);
     }
 
-    RetainPtr<UIImage> image = adoptNS([[UIImage alloc] initWithCGImage:imageRef.get()]);
-    return thumbnailSizedImageForImage(image.get());
+    return thumbnailSizedImageForImage(imageRef.get());
 }
 
 static UIImage* iconForFile(NSURL *file)
@@ -238,22 +244,10 @@
 
 
 @interface _WKImageFileUploadItem : _WKFileUploadItem
-- (instancetype)initWithFileURL:(NSURL *)fileURL originalImage:(UIImage *)originalImage;
 @end
 
-@implementation _WKImageFileUploadItem {
-    RetainPtr<UIImage> _originalImage;
-}
+@implementation _WKImageFileUploadItem
 
-- (instancetype)initWithFileURL:(NSURL *)fileURL originalImage:(UIImage *)originalImage
-{
-    if (!(self = [super initWithFileURL:fileURL]))
-        return nil;
-
-    _originalImage = originalImage;
-    return self;
-}
-
 - (BOOL)isVideo
 {
     return NO;
@@ -261,7 +255,7 @@
 
 - (UIImage *)displayImage
 {
-    return thumbnailSizedImageForImage(_originalImage.get());
+    return iconForImageFile(self.fileURL);
 }
 
 @end
@@ -735,10 +729,9 @@
     [self _uploadItemFromMediaInfo:info successBlock:uploadItemSuccessBlock failureBlock:failureBlock];
 }
 
-- (void)_uploadItemForImageData:(NSData *)imageData originalImage:(UIImage *)originalImage imageName:(NSString *)imageName successBlock:(void (^)(_WKFileUploadItem *))successBlock failureBlock:(void (^)(void))failureBlock
+- (void)_uploadItemForImageData:(NSData *)imageData imageName:(NSString *)imageName successBlock:(void (^)(_WKFileUploadItem *))successBlock failureBlock:(void (^)(void))failureBlock
 {
     ASSERT_ARG(imageData, imageData);
-    ASSERT_ARG(originalImage, originalImage);
     ASSERT(!isMainThread());
 
     NSString * const kTemporaryDirectoryName = @"WKWebFileUpload";
@@ -762,7 +755,7 @@
         return;
     }
 
-    successBlock(adoptNS([[_WKImageFileUploadItem alloc] initWithFileURL:[NSURL fileURLWithPath:filePath] originalImage:originalImage]).get());
+    successBlock(adoptNS([[_WKImageFileUploadItem alloc] initWithFileURL:[NSURL fileURLWithPath:filePath]]).get());
 }
 
 - (void)_uploadItemForJPEGRepresentationOfImage:(UIImage *)image successBlock:(void (^)(_WKFileUploadItem *))successBlock failureBlock:(void (^)(void))failureBlock
@@ -785,7 +778,7 @@
         // naming each of the individual uploads image.jpg? This won't work for photos taken with
         // the camera, but would work for photos picked from the library.
         NSString * const kUploadImageName = @"image.jpg";
-        [self _uploadItemForImageData:jpeg originalImage:image imageName:kUploadImageName successBlock:successBlock failureBlock:failureBlock];
+        [self _uploadItemForImageData:jpeg imageName:kUploadImageName successBlock:successBlock failureBlock:failureBlock];
     });
 }
 
@@ -817,7 +810,7 @@
 
             RetainPtr<CFStringRef> extension = adoptCF(UTTypeCopyPreferredTagWithClass((CFStringRef)dataUTI, kUTTagClassFilenameExtension));
             NSString *imageName = [@"image." stringByAppendingString:(extension ? (id)extension.get() : @"jpg")];
-            [self _uploadItemForImageData:imageData originalImage:image imageName:imageName successBlock:successBlock failureBlock:failureBlock];
+            [self _uploadItemForImageData:imageData imageName:imageName successBlock:successBlock failureBlock:failureBlock];
         }];
     });
 }
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to