Start sketching out image editor.
This commit is contained in:
parent
7245307c92
commit
26a25f861b
|
@ -235,6 +235,7 @@
|
|||
34BECE2B1F74C12700D7438D /* DebugUIStress.m in Sources */ = {isa = PBXBuildFile; fileRef = 34BECE2A1F74C12700D7438D /* DebugUIStress.m */; };
|
||||
34BECE2E1F7ABCE000D7438D /* GifPickerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34BECE2D1F7ABCE000D7438D /* GifPickerViewController.swift */; };
|
||||
34BECE301F7ABCF800D7438D /* GifPickerLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34BECE2F1F7ABCF800D7438D /* GifPickerLayout.swift */; };
|
||||
34BEDB0E21C405B0007B0EAE /* ImageEditor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34BEDB0D21C405B0007B0EAE /* ImageEditor.swift */; };
|
||||
34C3C78D20409F320000134C /* Opening.m4r in Resources */ = {isa = PBXBuildFile; fileRef = 34C3C78C20409F320000134C /* Opening.m4r */; };
|
||||
34C3C78F2040A4F70000134C /* sonarping.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 34C3C78E2040A4F70000134C /* sonarping.mp3 */; };
|
||||
34C3C7922040B0DD0000134C /* OWSAudioPlayer.h in Headers */ = {isa = PBXBuildFile; fileRef = 34C3C7902040B0DC0000134C /* OWSAudioPlayer.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
|
@ -906,6 +907,7 @@
|
|||
34BECE2A1F74C12700D7438D /* DebugUIStress.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DebugUIStress.m; sourceTree = "<group>"; };
|
||||
34BECE2D1F7ABCE000D7438D /* GifPickerViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GifPickerViewController.swift; sourceTree = "<group>"; };
|
||||
34BECE2F1F7ABCF800D7438D /* GifPickerLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GifPickerLayout.swift; sourceTree = "<group>"; };
|
||||
34BEDB0D21C405B0007B0EAE /* ImageEditor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageEditor.swift; sourceTree = "<group>"; };
|
||||
34C3C78C20409F320000134C /* Opening.m4r */ = {isa = PBXFileReference; lastKnownFileType = file; path = Opening.m4r; sourceTree = "<group>"; };
|
||||
34C3C78E2040A4F70000134C /* sonarping.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; name = sonarping.mp3; path = Signal/AudioFiles/sonarping.mp3; sourceTree = SOURCE_ROOT; };
|
||||
34C3C7902040B0DC0000134C /* OWSAudioPlayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSAudioPlayer.h; sourceTree = "<group>"; };
|
||||
|
@ -1714,7 +1716,9 @@
|
|||
34AC0A00211B39E700997B47 /* DisappearingTimerConfigurationView.swift */,
|
||||
4CA46F49219C78050038ABDE /* GalleryRailView.swift */,
|
||||
34AC0A08211B39E900997B47 /* GradientView.swift */,
|
||||
34BEDB0C21C405B0007B0EAE /* ImageEditor */,
|
||||
34AC0A06211B39E900997B47 /* OWSAlerts.swift */,
|
||||
4C618198219DF03A009BD6B5 /* OWSButton.swift */,
|
||||
34AC0A09211B39E900997B47 /* OWSFlatButton.swift */,
|
||||
34AC09FE211B39E700997B47 /* OWSLayerView.swift */,
|
||||
34AC0A03211B39E800997B47 /* OWSNavigationBar.swift */,
|
||||
|
@ -1729,7 +1733,6 @@
|
|||
34AC0A0D211B39EA00997B47 /* ThreadViewHelper.h */,
|
||||
34AC0A0B211B39EA00997B47 /* ThreadViewHelper.m */,
|
||||
34AC0A04211B39E800997B47 /* VideoPlayerView.swift */,
|
||||
4C618198219DF03A009BD6B5 /* OWSButton.swift */,
|
||||
);
|
||||
path = Views;
|
||||
sourceTree = "<group>";
|
||||
|
@ -1858,6 +1861,14 @@
|
|||
path = GifPicker;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
34BEDB0C21C405B0007B0EAE /* ImageEditor */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
34BEDB0D21C405B0007B0EAE /* ImageEditor.swift */,
|
||||
);
|
||||
path = ImageEditor;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
34C3C78B20409F320000134C /* ringtoneSounds */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -3323,6 +3334,7 @@
|
|||
34AC09E9211B39B100997B47 /* OWSTableViewController.m in Sources */,
|
||||
346129F51FD5F31400532771 /* OWS102MoveLoggingPreferenceToUserDefaults.m in Sources */,
|
||||
45194F8F1FD71FF500333B2C /* ThreadUtil.m in Sources */,
|
||||
34BEDB0E21C405B0007B0EAE /* ImageEditor.swift in Sources */,
|
||||
451F8A3B1FD71297005CB9DA /* UIUtil.m in Sources */,
|
||||
450C800F20AD1AB900F3A091 /* OWSWindowManager.m in Sources */,
|
||||
454A965A1FD6017E008D2A0E /* SignalAttachment.swift in Sources */,
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
@objc public enum ImageEditorError: Int, Error {
|
||||
case assertionError
|
||||
case invalidInput
|
||||
}
|
||||
|
||||
@objc
|
||||
public class ImageEditorItem: NSObject {
|
||||
@objc
|
||||
public let itemId: String
|
||||
|
||||
@objc
|
||||
public override required init() {
|
||||
self.itemId = UUID().uuidString
|
||||
|
||||
super.init()
|
||||
}
|
||||
}
|
||||
|
||||
@objc
|
||||
public class ImageEditorModel: NSObject {
|
||||
private let srcImagePath: String
|
||||
private let srcImageSize: CGSize
|
||||
|
||||
@objc
|
||||
public required init(srcImagePath: String) throws {
|
||||
self.srcImagePath = srcImagePath
|
||||
|
||||
let srcFileName = (srcImagePath as NSString).lastPathComponent
|
||||
let srcFileExtension = (srcFileName as NSString).pathExtension
|
||||
guard let mimeType = MIMETypeUtil.mimeType(forFileExtension: srcFileExtension) else {
|
||||
Logger.error("Couldn't determine MIME type for file.")
|
||||
throw ImageEditorError.invalidInput
|
||||
}
|
||||
guard MIMETypeUtil.isImage(mimeType) else {
|
||||
Logger.error("Invalid MIME type: \(mimeType).")
|
||||
throw ImageEditorError.invalidInput
|
||||
}
|
||||
|
||||
let srcImageSize = NSData.imageSize(forFilePath: srcImagePath, mimeType: mimeType)
|
||||
guard srcImageSize.width > 0, srcImageSize.height > 0 else {
|
||||
Logger.error("Couldn't determine image size.")
|
||||
throw ImageEditorError.invalidInput
|
||||
}
|
||||
self.srcImageSize = srcImageSize
|
||||
|
||||
super.init()
|
||||
}
|
||||
}
|
|
@ -494,68 +494,13 @@ typedef void (^OWSLoadedThumbnailSuccess)(OWSLoadedThumbnail *loadedThumbnail);
|
|||
}
|
||||
return [self videoStillImage].size;
|
||||
} else if ([self isImage] || [self isAnimated]) {
|
||||
NSURL *_Nullable mediaUrl = self.originalMediaURL;
|
||||
if (!mediaUrl) {
|
||||
return CGSizeZero;
|
||||
}
|
||||
if (![self isValidImage]) {
|
||||
return CGSizeZero;
|
||||
}
|
||||
|
||||
// With CGImageSource we avoid loading the whole image into memory.
|
||||
CGImageSourceRef source = CGImageSourceCreateWithURL((CFURLRef)mediaUrl, NULL);
|
||||
if (!source) {
|
||||
OWSFailDebug(@"Could not load image: %@", mediaUrl);
|
||||
return CGSizeZero;
|
||||
}
|
||||
|
||||
NSDictionary *options = @{
|
||||
(NSString *)kCGImageSourceShouldCache : @(NO),
|
||||
};
|
||||
NSDictionary *properties
|
||||
= (__bridge_transfer NSDictionary *)CGImageSourceCopyPropertiesAtIndex(source, 0, (CFDictionaryRef)options);
|
||||
CGSize imageSize = CGSizeZero;
|
||||
if (properties) {
|
||||
NSNumber *orientation = properties[(NSString *)kCGImagePropertyOrientation];
|
||||
NSNumber *width = properties[(NSString *)kCGImagePropertyPixelWidth];
|
||||
NSNumber *height = properties[(NSString *)kCGImagePropertyPixelHeight];
|
||||
|
||||
if (width && height) {
|
||||
imageSize = CGSizeMake(width.floatValue, height.floatValue);
|
||||
|
||||
if (orientation) {
|
||||
imageSize =
|
||||
[self applyImageOrientation:(UIImageOrientation)orientation.intValue toImageSize:imageSize];
|
||||
}
|
||||
} else {
|
||||
OWSFailDebug(@"Could not determine size of image: %@", mediaUrl);
|
||||
}
|
||||
}
|
||||
CFRelease(source);
|
||||
return imageSize;
|
||||
// imageSizeForFilePath checks validity.
|
||||
return [NSData imageSizeForFilePath:self.originalFilePath mimeType:self.contentType];
|
||||
} else {
|
||||
return CGSizeZero;
|
||||
}
|
||||
}
|
||||
|
||||
- (CGSize)applyImageOrientation:(UIImageOrientation)orientation toImageSize:(CGSize)imageSize
|
||||
{
|
||||
switch (orientation) {
|
||||
case UIImageOrientationUp: // EXIF = 1
|
||||
case UIImageOrientationUpMirrored: // EXIF = 2
|
||||
case UIImageOrientationDown: // EXIF = 3
|
||||
case UIImageOrientationDownMirrored: // EXIF = 4
|
||||
return imageSize;
|
||||
case UIImageOrientationLeftMirrored: // EXIF = 5
|
||||
case UIImageOrientationLeft: // EXIF = 6
|
||||
case UIImageOrientationRightMirrored: // EXIF = 7
|
||||
case UIImageOrientationRight: // EXIF = 8
|
||||
return CGSizeMake(imageSize.height, imageSize.width);
|
||||
default:
|
||||
return imageSize;
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)shouldHaveImageSize
|
||||
{
|
||||
return ([self isVideo] || [self isImage] || [self isAnimated]);
|
||||
|
|
|
@ -11,4 +11,7 @@
|
|||
- (BOOL)ows_isValidImage;
|
||||
- (BOOL)ows_isValidImageWithMimeType:(nullable NSString *)mimeType;
|
||||
|
||||
// Returns CGSizeZero on error.
|
||||
+ (CGSize)imageSizeForFilePath:(NSString *)filePath mimeType:(NSString *)mimeType;
|
||||
|
||||
@end
|
||||
|
|
|
@ -312,4 +312,62 @@ typedef NS_ENUM(NSInteger, ImageFormat) {
|
|||
return (width > 0 && width < kMaxValidSize && height > 0 && height < kMaxValidSize);
|
||||
}
|
||||
|
||||
+ (CGSize)imageSizeForFilePath:(NSString *)filePath mimeType:(NSString *)mimeType
|
||||
{
|
||||
if (![NSData ows_isValidImageAtPath:filePath mimeType:mimeType]) {
|
||||
OWSLogError(@"Invalid image.");
|
||||
return CGSizeZero;
|
||||
}
|
||||
NSURL *url = [NSURL fileURLWithPath:filePath];
|
||||
|
||||
// With CGImageSource we avoid loading the whole image into memory.
|
||||
CGImageSourceRef source = CGImageSourceCreateWithURL((CFURLRef)url, NULL);
|
||||
if (!source) {
|
||||
OWSFailDebug(@"Could not load image: %@", url);
|
||||
return CGSizeZero;
|
||||
}
|
||||
|
||||
NSDictionary *options = @{
|
||||
(NSString *)kCGImageSourceShouldCache : @(NO),
|
||||
};
|
||||
NSDictionary *properties
|
||||
= (__bridge_transfer NSDictionary *)CGImageSourceCopyPropertiesAtIndex(source, 0, (CFDictionaryRef)options);
|
||||
CGSize imageSize = CGSizeZero;
|
||||
if (properties) {
|
||||
NSNumber *orientation = properties[(NSString *)kCGImagePropertyOrientation];
|
||||
NSNumber *width = properties[(NSString *)kCGImagePropertyPixelWidth];
|
||||
NSNumber *height = properties[(NSString *)kCGImagePropertyPixelHeight];
|
||||
|
||||
if (width && height) {
|
||||
imageSize = CGSizeMake(width.floatValue, height.floatValue);
|
||||
|
||||
if (orientation) {
|
||||
imageSize = [self applyImageOrientation:(UIImageOrientation)orientation.intValue toImageSize:imageSize];
|
||||
}
|
||||
} else {
|
||||
OWSFailDebug(@"Could not determine size of image: %@", url);
|
||||
}
|
||||
}
|
||||
CFRelease(source);
|
||||
return imageSize;
|
||||
}
|
||||
|
||||
+ (CGSize)applyImageOrientation:(UIImageOrientation)orientation toImageSize:(CGSize)imageSize
|
||||
{
|
||||
switch (orientation) {
|
||||
case UIImageOrientationUp: // EXIF = 1
|
||||
case UIImageOrientationUpMirrored: // EXIF = 2
|
||||
case UIImageOrientationDown: // EXIF = 3
|
||||
case UIImageOrientationDownMirrored: // EXIF = 4
|
||||
return imageSize;
|
||||
case UIImageOrientationLeftMirrored: // EXIF = 5
|
||||
case UIImageOrientationLeft: // EXIF = 6
|
||||
case UIImageOrientationRightMirrored: // EXIF = 7
|
||||
case UIImageOrientationRight: // EXIF = 8
|
||||
return CGSizeMake(imageSize.height, imageSize.width);
|
||||
default:
|
||||
return imageSize;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
Loading…
Reference in New Issue