diff --git a/Tusker/Caching/ImageCache.swift b/Tusker/Caching/ImageCache.swift index f8c8a5e8..f872f5e0 100644 --- a/Tusker/Caching/ImageCache.swift +++ b/Tusker/Caching/ImageCache.swift @@ -34,10 +34,10 @@ class ImageCache { self.cache = ImageDataCache(name: name, memoryExpiry: memoryExpiry, diskExpiry: diskExpiry, storeOriginalDataInMemory: diskExpiry == nil, desiredPixelSize: pixelSize) } - func get(_ url: URL, completion: ((Data?, UIImage?) -> Void)?) -> Request? { + func get(_ url: URL, loadOriginal: Bool = false, completion: ((Data?, UIImage?) -> Void)?) -> Request? { let key = url.absoluteString if !ImageCache.disableCaching, - let entry = try? cache.get(key) { + let entry = try? cache.get(key, loadOriginal: loadOriginal) { if let completion = completion { backgroundQueue.async { completion(entry.data, entry.image) @@ -73,7 +73,7 @@ class ImageCache { private func createGroup(url: URL) -> RequestGroup { let group = RequestGroup(url: url) { (data, image) in if let data = data { - try? self.cache.set(url.absoluteString, data: data) + try? self.cache.set(url.absoluteString, data: data, image: image) } self.groups.removeValueWithoutReturning(forKey: url) } @@ -85,8 +85,8 @@ class ImageCache { return try? cache.getData(url.absoluteString) } - func get(_ url: URL) -> ImageDataCache.Entry? { - return try? cache.get(url.absoluteString) + func get(_ url: URL, loadOriginal: Bool = false) -> ImageDataCache.Entry? { + return try? cache.get(url.absoluteString, loadOriginal: loadOriginal) } func cancelWithoutCallback(_ url: URL) { diff --git a/Tusker/Caching/ImageDataCache.swift b/Tusker/Caching/ImageDataCache.swift index ead9ea23..8e3cf77c 100644 --- a/Tusker/Caching/ImageDataCache.swift +++ b/Tusker/Caching/ImageDataCache.swift @@ -41,8 +41,9 @@ class ImageDataCache { } } - func get(_ key: String) throws -> Entry? { - if let memoryEntry = try? memory.object(forKey: key) { + func get(_ key: String, loadOriginal: Bool) throws -> Entry? { + if storeOriginalDataInMemory || !loadOriginal, + let memoryEntry = try? memory.object(forKey: key) { return memoryEntry } else if let disk = self.disk, let data = try? disk.object(forKey: key), @@ -54,15 +55,15 @@ class ImageDataCache { } func getImage(_ key: String) throws -> UIImage? { - return try get(key)?.image + return try get(key, loadOriginal: false)?.image } func getData(_ key: String) throws -> Data? { - return try get(key)?.data + return try get(key, loadOriginal: false)?.data } - func set(_ key: String, data: Data) throws { - guard let image = scaleImageIfDesired(data: data) else { return } + func set(_ key: String, data: Data, image: UIImage?) throws { + guard let image = scaleImageIfDesired(data: data) ?? image else { return } let entry = Entry(data: storeOriginalDataInMemory ? data : nil, image: image) memory.setObject(entry, forKey: key) @@ -80,7 +81,7 @@ class ImageDataCache { private func scaleImageIfDesired(data: Data) -> UIImage? { guard let desiredPixelSize = desiredPixelSize, let source = CGImageSourceCreateWithData(data as CFData, [kCGImageSourceShouldCache: false] as CFDictionary) else { - return UIImage(data: data) + return nil } let maxDimension = max(desiredPixelSize.width, desiredPixelSize.height) @@ -94,7 +95,7 @@ class ImageDataCache { if let downsampled = CGImageSourceCreateThumbnailAtIndex(source, 0, downsampleOptions) { return UIImage(cgImage: downsampled) } else { - return UIImage(data: data) + return nil } } diff --git a/Tusker/Screens/Large Image/LoadingLargeImageViewController.swift b/Tusker/Screens/Large Image/LoadingLargeImageViewController.swift index 997ca4cd..08cdf948 100644 --- a/Tusker/Screens/Large Image/LoadingLargeImageViewController.swift +++ b/Tusker/Screens/Large Image/LoadingLargeImageViewController.swift @@ -85,14 +85,15 @@ class LoadingLargeImageViewController: UIViewController, LargeImageAnimatableVie overrideUserInterfaceStyle = .dark view.backgroundColor = .black - if let entry = cache.get(url) { + // always load full resolution from disk for large image, in case the cache is scaled + if let entry = cache.get(url, loadOriginal: true) { createLargeImage(data: entry.data, image: entry.image, url: url) } else { createPreview() loadingVC = LoadingViewController() embedChild(loadingVC!) - imageRequest = cache.get(url) { [weak self] (data, image) in + imageRequest = cache.get(url, loadOriginal: true) { [weak self] (data, image) in guard let self = self else { return } self.imageRequest = nil DispatchQueue.main.async { diff --git a/Tusker/Views/Profile Header/ProfileHeaderView.swift b/Tusker/Views/Profile Header/ProfileHeaderView.swift index 0e9fd4ea..6940a67d 100644 --- a/Tusker/Views/Profile Header/ProfileHeaderView.swift +++ b/Tusker/Views/Profile Header/ProfileHeaderView.swift @@ -191,16 +191,15 @@ class ProfileHeaderView: UIView { let accountID = account.id let avatarURL = account.avatar - avatarRequest = ImageCache.avatars.get(avatarURL) { [weak self] (_, image) in - guard let self = self, let image = image, self.accountID == accountID else { return } - self.avatarRequest = nil - - let transformedImage: UIImage? - if self.isGrayscale { - transformedImage = ImageGrayscalifier.convert(url: avatarURL, cgImage: image.cgImage!) - } else { - transformedImage = image + // always load original for avatars, because ImageCache.avatars stores them scaled-down in memory + avatarRequest = ImageCache.avatars.get(avatarURL, loadOriginal: true) { [weak self] (_, image) in + guard let self = self, + let image = image, + self.accountID == accountID, + let transformedImage = ImageGrayscalifier.convertIfNecessary(url: avatarURL, image: image) else { + return } + self.avatarRequest = nil DispatchQueue.main.async { self.avatarImageView.image = transformedImage @@ -208,15 +207,13 @@ class ProfileHeaderView: UIView { } if let header = account.header { headerRequest = ImageCache.headers.get(header) { [weak self] (_, image) in - guard let self = self, let image = image, self.accountID == accountID else { return } - self.headerRequest = nil - - let transformedImage: UIImage? - if self.isGrayscale { - transformedImage = ImageGrayscalifier.convert(url: header, cgImage: image.cgImage!) - } else { - transformedImage = image + guard let self = self, + let image = image, + self.accountID == accountID, + let transformedImage = ImageGrayscalifier.convertIfNecessary(url: header, image: image) else { + return } + self.headerRequest = nil DispatchQueue.main.async { self.headerImageView.image = transformedImage