Compare commits
3 Commits
master
..
610700840a
| Author | SHA1 | Date | |
|---|---|---|---|
|
610700840a
|
|||
|
65d53a34af
|
|||
|
dd7528dfe4
|
+161
-175
@@ -1,6 +1,7 @@
|
||||
// TODO: Anilist support?
|
||||
// TODO: Properly avoid swallowing of input from UICollectionView used for scrolling
|
||||
// TODO: Convert between state for normal and scrolling page turn
|
||||
// TODO: Support reading with scrolling and landscape mode
|
||||
|
||||
import FlatBuffers
|
||||
import Foundation
|
||||
@@ -193,10 +194,9 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate {
|
||||
metadataList[dir] = metadata
|
||||
loadLocalState()
|
||||
let volCovP = ProgressIndices(v: progress.v, c: 0, i: 0)
|
||||
let archivePath = getArchiveURL(volCovP.v)
|
||||
let archive = try! Archive(url: archivePath, accessMode: .read)
|
||||
let filename = metadata.volumes[volCovP.v].chapters[volCovP.c].images[volCovP.i].filename!
|
||||
let targetEntry = archive[filename]!
|
||||
let volumeDir = currentPath.appendingPathComponent("volumes")
|
||||
let archive = try! Archive(url: volumeDir.appendingPathComponent(metadata.volumes[progress.v].archive), accessMode: .read)
|
||||
let targetEntry = archive[metadata.volumes[volCovP.v].chapters[volCovP.c].images[volCovP.i].filename]!
|
||||
var extractedData = Data()
|
||||
_ = try archive.extract(targetEntry) { data in
|
||||
extractedData.append(data)
|
||||
@@ -206,8 +206,7 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate {
|
||||
cover: UIImage(data: extractedData)!,
|
||||
metadata: metadata,
|
||||
path: dir
|
||||
)
|
||||
)
|
||||
))
|
||||
}
|
||||
} catch {
|
||||
print("Failed to read directories")
|
||||
@@ -266,7 +265,7 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate {
|
||||
func getMetadataFromFilename(path: URL) {
|
||||
// Beautiful, is it not?
|
||||
let patterns: [(NSRegularExpression, NSRegularExpression, Int, Int, Int)] = [
|
||||
(try! NSRegularExpression(pattern: #"c([0-9]+(?:x[0-9]+)?) \(v([0-9]+)\) - p([0-9]+(?:-p?[0-9]+)?)"#), try! NSRegularExpression(pattern: #"^(.*) - c([0-9]+(?:x[0-9]+)?) \(v([0-9]+)\) - p([0-9]+(?:-p?[0-9]+)?)"#), 3, 1, 2),
|
||||
(try! NSRegularExpression(pattern: #"c([0-9]+(?:x[0-9]+)?) \(v([0-9]+)\) - p([0-9]+(?:-[0-9]+)?)"#), try! NSRegularExpression(pattern: #"^(.*) - c([0-9]+(?:x[0-9]+)?) \(v([0-9]+)\) - p([0-9]+(?:-[0-9]+)?)"#), 3, 1, 2),
|
||||
(try! NSRegularExpression(pattern: "v([0-9]+) p([0-9]+(?:-[0-9]+)?) - c([0-9]+(?:x[0-9]+)?)"), try! NSRegularExpression(pattern: "^(.*) - v([0-9]+) p([0-9]+(?:-[0-9]+)?) - c([0-9]+(?:x[0-9]+)?)"), 2, 3, 1),
|
||||
]
|
||||
var pattern: NSRegularExpression! = nil
|
||||
@@ -291,7 +290,6 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate {
|
||||
var volumes: [Offset] = []
|
||||
var currentChapters: [Offset] = []
|
||||
var currentImages: [Offset] = []
|
||||
var previousVolumeURL = ""
|
||||
|
||||
var titleValue: String = ""
|
||||
|
||||
@@ -307,7 +305,6 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate {
|
||||
includingPropertiesForKeys: [.isRegularFileKey],
|
||||
options: [.skipsHiddenFiles]
|
||||
).filter { $0.pathExtension == "cbz" }
|
||||
.sorted { $0.lastPathComponent < $1.lastPathComponent }
|
||||
|
||||
if volumeURLs.isEmpty {
|
||||
print("found no volumes in volume dir: \(volumeDir)")
|
||||
@@ -330,6 +327,8 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate {
|
||||
return
|
||||
}
|
||||
|
||||
print("example filename in archive: \(filenames[0].1), \(filenames[1].1)")
|
||||
|
||||
do {
|
||||
for (currentPattern, currentTitlePattern, currentPageRegexPosition, currentChapterRegexPosition, currentVolumeRegexPosition) in patterns {
|
||||
let filename = filenames[0].1
|
||||
@@ -376,6 +375,7 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
let regex = pattern!
|
||||
let titleRegex = titlePattern!
|
||||
|
||||
@@ -403,7 +403,7 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate {
|
||||
let (page, doublePage) = (
|
||||
UInt32(pageParts[0])!, pageParts.count > 1
|
||||
)
|
||||
|
||||
|
||||
let archive = try Archive(url: volumeDir.appendingPathComponent(volumeURL), accessMode: .read)
|
||||
let entry = archive[filename]!
|
||||
var size = ImageDimensions(width: 0, height: 0)
|
||||
@@ -414,10 +414,10 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate {
|
||||
return
|
||||
}
|
||||
let m_filename = builder.create(string: filename)
|
||||
let imageMetadata = ImageMetadata.createImageMetadata(&builder,
|
||||
doublePage: doublePage,
|
||||
filenameOffset: m_filename,
|
||||
firstPage: page,
|
||||
let imageMetadata = ImageMetadata.createImageMetadata(&builder,
|
||||
doublePage: doublePage,
|
||||
filenameOffset: m_filename,
|
||||
firstPage: page,
|
||||
size: Size(width: UInt32(size.width), height: UInt32(size.height))
|
||||
)
|
||||
|
||||
@@ -427,7 +427,7 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate {
|
||||
|
||||
let chapterName = builder.create(string: "")
|
||||
let imagesOffset = builder.createVector(ofOffsets: currentImages)
|
||||
let chapterMetadata = ChapterMetadata.createChapterMetadata(&builder,
|
||||
let chapterMetadata = ChapterMetadata.createChapterMetadata(&builder,
|
||||
chapter: MetaValue(
|
||||
main: currentChapter.0, bonus: currentChapter.1 ?? 0
|
||||
),
|
||||
@@ -438,12 +438,12 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate {
|
||||
currentChapters.append(chapterMetadata)
|
||||
|
||||
let volumeTitle = builder.create(string: "")
|
||||
let volumeURLOffset = builder.create(string: previousVolumeURL)
|
||||
let volumeURLOffset = builder.create(string: volumeURL)
|
||||
let chaptersOffset = builder.createVector(ofOffsets: currentChapters)
|
||||
let volumeMetadata = VolumeMetadata.createVolumeMetadata(
|
||||
&builder,
|
||||
volume: MetaValue(main: currentVolume, bonus: 0),
|
||||
titleOffset: volumeTitle,
|
||||
volume: MetaValue(main: currentVolume, bonus: 0),
|
||||
titleOffset: volumeTitle,
|
||||
archiveOffset: volumeURLOffset,
|
||||
chaptersVectorOffset: chaptersOffset,
|
||||
)
|
||||
@@ -461,7 +461,7 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate {
|
||||
|
||||
let chapterName = builder.create(string: "")
|
||||
let imagesOffset = builder.createVector(ofOffsets: currentImages)
|
||||
let chapterMetadata = ChapterMetadata.createChapterMetadata(&builder,
|
||||
let chapterMetadata = ChapterMetadata.createChapterMetadata(&builder,
|
||||
chapter: MetaValue(
|
||||
main: currentChapter.0, bonus: currentChapter.1 ?? 0
|
||||
),
|
||||
@@ -482,7 +482,7 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate {
|
||||
if (i == filenames.count - 1) {
|
||||
let chapterName = builder.create(string: "")
|
||||
let imagesOffset = builder.createVector(ofOffsets: currentImages)
|
||||
let chapterMetadata = ChapterMetadata.createChapterMetadata(&builder,
|
||||
let chapterMetadata = ChapterMetadata.createChapterMetadata(&builder,
|
||||
chapter: MetaValue(
|
||||
main: chapter.0, bonus: chapter.1 ?? 0
|
||||
),
|
||||
@@ -497,8 +497,8 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate {
|
||||
let chaptersOffset = builder.createVector(ofOffsets: currentChapters)
|
||||
let volumeMetadata = VolumeMetadata.createVolumeMetadata(
|
||||
&builder,
|
||||
volume: MetaValue(main: volume, bonus: 0),
|
||||
titleOffset: volumeTitle,
|
||||
volume: MetaValue(main: volume, bonus: 0),
|
||||
titleOffset: volumeTitle,
|
||||
archiveOffset: volumeURLOffset,
|
||||
chaptersVectorOffset: chaptersOffset
|
||||
)
|
||||
@@ -510,7 +510,6 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate {
|
||||
}
|
||||
currentVolume = volume
|
||||
currentChapter = chapter
|
||||
previousVolumeURL = volumeURL
|
||||
} else {
|
||||
print("no regex match on \(filename), returning")
|
||||
return
|
||||
@@ -692,35 +691,73 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate {
|
||||
var offset = scrollingCollectionView.contentOffset
|
||||
offset.y *= (screenSize.width / screenSize.height)
|
||||
scrollingCollectionView.setContentOffset(offset, animated: false)
|
||||
// FIXME: This is a caching issue, needs a different cache for scrolling since it Fill instead of Fit.
|
||||
scrollingCollectionView.reloadData()
|
||||
}
|
||||
}
|
||||
|
||||
func readComic(name: String) {
|
||||
imageView.image = nil
|
||||
readerView.isHidden = false
|
||||
homeView.isHidden = true
|
||||
setNeedsStatusBarAppearanceUpdate()
|
||||
if let path = getPathFromComicName(name: name) {
|
||||
currentPath = path
|
||||
|
||||
metadata = metadataList[path]
|
||||
|
||||
globalState.comicName = metadata.title
|
||||
saveGlobalState()
|
||||
loadLocalState()
|
||||
scrollingCollectionView.isHidden = mode != .scroll
|
||||
leftView.isHidden = mode == .scroll
|
||||
rightView.isHidden = mode == .scroll
|
||||
imageView.isHidden = mode == .scroll
|
||||
if mode == .scroll {
|
||||
scrollingCollectionView.reloadData()
|
||||
if mode != .scroll {
|
||||
scrollingCollectionView.isHidden = true
|
||||
leftView.isHidden = false
|
||||
rightView.isHidden = false
|
||||
setImages(path: path)
|
||||
} else {
|
||||
setImages()
|
||||
setupScrolling()
|
||||
}
|
||||
} else {
|
||||
print("path not found")
|
||||
}
|
||||
}
|
||||
|
||||
func setupScrolling() {
|
||||
leftView.isHidden = true
|
||||
rightView.isHidden = true
|
||||
scrollingCollectionView.isHidden = false
|
||||
scrollingCollectionView.reloadData()
|
||||
}
|
||||
|
||||
func countFiles() -> Int {
|
||||
var count = 0
|
||||
if let enumerator = fileManager.enumerator(
|
||||
at: currentPath, includingPropertiesForKeys: [.isDirectoryKey],
|
||||
options: [.skipsHiddenFiles]
|
||||
) {
|
||||
for case let dir as URL in enumerator {
|
||||
do {
|
||||
if let enumerator = fileManager.enumerator(
|
||||
at: dir, includingPropertiesForKeys: [.isRegularFileKey],
|
||||
options: [.skipsHiddenFiles]
|
||||
) {
|
||||
for case let file as URL in enumerator {
|
||||
let resourceValues = try file.resourceValues(forKeys: [
|
||||
.isRegularFileKey,
|
||||
])
|
||||
if resourceValues.isRegularFile == true {
|
||||
count += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} catch {
|
||||
print("Error reading file attributes for \(dir):", error)
|
||||
}
|
||||
}
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
func setupScrollingCollectionView() {
|
||||
let layout = UICollectionViewFlowLayout()
|
||||
layout.minimumInteritemSpacing = 0
|
||||
@@ -1007,11 +1044,13 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate {
|
||||
default: break
|
||||
}
|
||||
if prev == mode { return }
|
||||
if mode != .scroll {
|
||||
let archiveURL = getArchiveURL(progress.v)
|
||||
if mode == .scroll {
|
||||
setupScrolling()
|
||||
} else {
|
||||
let archiveURL = currentPath.appendingPathComponent("volumes").appendingPathComponent(metadata.volumes[progress.v].archive)
|
||||
imageLoader.loadImage(
|
||||
archiveURL: archiveURL,
|
||||
filename: getImagePath(progress),
|
||||
filename: getImagePath(progress: progress),
|
||||
scaling: .scaleAspectFit,
|
||||
screenSize: UIScreen.main.bounds.size
|
||||
) { [weak self] image in
|
||||
@@ -1023,10 +1062,6 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate {
|
||||
leftView.isHidden = mode == .scroll
|
||||
rightView.isHidden = mode == .scroll
|
||||
imageView.isHidden = mode == .scroll
|
||||
// This does not optmize for the case where you switch between scrolling and non-scrolling in the same comic. However this badly supported because of the lac of state conversion.
|
||||
if mode == .scroll {
|
||||
scrollingCollectionView.reloadData()
|
||||
}
|
||||
togglePageTurnDropdown()
|
||||
saveLocalState()
|
||||
}
|
||||
@@ -1155,27 +1190,21 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate {
|
||||
}
|
||||
progress = newProgress
|
||||
updateInfo()
|
||||
let path = getImagePath(progress)
|
||||
let path = getImagePath(progress: progress)
|
||||
|
||||
imageLoader.setActiveWindow([path])
|
||||
|
||||
imageLoader.loadImage(
|
||||
archiveURL: getArchiveURL(progress.v),
|
||||
filename: getImagePath(progress),
|
||||
scaling: scaling,
|
||||
screenSize: UIScreen.main.bounds.size
|
||||
) { [weak self] image in
|
||||
let archiveURL = currentPath.appendingPathComponent("volumes").appendingPathComponent(metadata.volumes[progress.v].archive)
|
||||
imageLoader.loadImage(archiveURL: archiveURL, filename: getImagePath(progress: progress), scaling: scaling, screenSize: UIScreen.main.bounds.size) { [weak self] image in
|
||||
self?.imageView.image = image
|
||||
}
|
||||
|
||||
for _ in 0 ..< preloadCount {
|
||||
newProgress = getProgressIndicesFromTurn(turn: .next, progress: newProgress)
|
||||
imageLoader.preloadImage(
|
||||
archiveURL: getArchiveURL(newProgress.v),
|
||||
filename: getImagePath(newProgress),
|
||||
scaling: scaling,
|
||||
screenSize: UIScreen.main.bounds.size
|
||||
)
|
||||
let archive = metadata.volumes[newProgress.v].archive!
|
||||
let filename = getImagePath(progress: newProgress)
|
||||
let archiveURL = currentPath.appendingPathComponent("volumes").appendingPathComponent(archive)
|
||||
imageLoader.preloadImage(archiveURL: archiveURL, filename: filename, scaling: scaling, screenSize: UIScreen.main.bounds.size)
|
||||
}
|
||||
|
||||
saveLocalState()
|
||||
@@ -1244,11 +1273,7 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate {
|
||||
info.text = text
|
||||
}
|
||||
|
||||
func getArchiveURL(_ volumeProgress: Int) -> URL {
|
||||
return currentPath.appendingPathComponent("volumes").appendingPathComponent(metadata.volumes[volumeProgress].archive)
|
||||
}
|
||||
|
||||
func getImagePath(_ progress: ProgressIndices) -> String {
|
||||
func getImagePath(progress: ProgressIndices) -> String {
|
||||
let (v, c, i) = (progress.v, progress.c, progress.i)
|
||||
let modernPath = metadata.volumes[v].chapters[c].images[i].filename!
|
||||
// if fileManager.fileExists(atPath: modernPath.path) {
|
||||
@@ -1298,27 +1323,25 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate {
|
||||
return metadata
|
||||
}
|
||||
|
||||
func setImages() {
|
||||
let scaling: UIView.ContentMode = mode == .scroll ? .scaleAspectFill : .scaleAspectFit
|
||||
func setImages(path _: URL) {
|
||||
let scaling: UIView.ContentMode!
|
||||
if mode == .scroll {
|
||||
scaling = UIView.ContentMode.scaleAspectFill
|
||||
} else { scaling = UIView.ContentMode.scaleAspectFit }
|
||||
imageLoader.loadImage(
|
||||
archiveURL: getArchiveURL(progress.v),
|
||||
filename: getImagePath(progress),
|
||||
archiveURL: currentPath.appendingPathComponent("volumes").appendingPathComponent(metadata.volumes[progress.v].archive),
|
||||
filename: getImagePath(progress: progress),
|
||||
scaling: scaling,
|
||||
screenSize: UIScreen.main.bounds.size
|
||||
) { [weak self] image in
|
||||
self?.imageView.image = image
|
||||
self?.updateInfo()
|
||||
}
|
||||
updateInfo()
|
||||
|
||||
var newProgress = progress
|
||||
for _ in 0 ..< preloadCount {
|
||||
newProgress = getProgressIndicesFromTurn(turn: .next, progress: newProgress)
|
||||
imageLoader.preloadImage(
|
||||
archiveURL: getArchiveURL(newProgress.v),
|
||||
filename: getImagePath(newProgress),
|
||||
scaling: scaling,
|
||||
screenSize: UIScreen.main.bounds.size,
|
||||
)
|
||||
imageLoader.preloadImage(archiveURL: currentPath.appendingPathComponent("volumes").appendingPathComponent(metadata.volumes[progress.v].archive), filename: getImagePath(progress: newProgress), scaling: scaling, screenSize: UIScreen.main.bounds.size)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1337,14 +1360,15 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate {
|
||||
super.viewWillTransition(to: size, with: coordinator)
|
||||
if mode != .scroll {
|
||||
imageLoader.loadImage(
|
||||
archiveURL: getArchiveURL(progress.v),
|
||||
filename: getImagePath(progress),
|
||||
archiveURL: currentPath.appendingPathComponent("volumes").appendingPathComponent(metadata.volumes[progress.v].archive),
|
||||
filename: getImagePath(progress: progress),
|
||||
scaling: .scaleAspectFit,
|
||||
screenSize: size
|
||||
) { image in
|
||||
self.imageView.image = image
|
||||
}
|
||||
} else {
|
||||
scrollingCollectionView.reloadData()
|
||||
changedOrientation = true
|
||||
}
|
||||
}
|
||||
@@ -1421,7 +1445,6 @@ extension ViewController: UICollectionViewDataSource, UICollectionViewDelegateFl
|
||||
withReuseIdentifier: "ScrollingImageCell", for: indexPath
|
||||
)
|
||||
as! ScrollingImageCell
|
||||
if mode != .scroll { return cell }
|
||||
if metadata == nil {
|
||||
print("metadata is nil, should probably not be the case")
|
||||
return cell
|
||||
@@ -1433,10 +1456,9 @@ extension ViewController: UICollectionViewDataSource, UICollectionViewDelegateFl
|
||||
}
|
||||
}
|
||||
imageLoader.loadImage(
|
||||
archiveURL: getArchiveURL(progress.v),
|
||||
filename: getImagePath(newProgress),
|
||||
scaling: scaling, screenSize: UIScreen.main.bounds.size
|
||||
) { image in
|
||||
archiveURL: currentPath.appendingPathComponent("volumes").appendingPathComponent(metadata.volumes[progress.v].archive),
|
||||
filename: getImagePath(progress: newProgress),
|
||||
scaling: scaling, screenSize: UIScreen.main.bounds.size) { image in
|
||||
cell.imageView.image = image
|
||||
}
|
||||
return cell
|
||||
@@ -1550,21 +1572,15 @@ enum Rotation {
|
||||
|
||||
class ImageLoader {
|
||||
private let cache = NSCache<NSString, UIImage>()
|
||||
private let portraitFitCache = NSCache<NSString, UIImage>()
|
||||
private let landscapeFitCache = NSCache<NSString, UIImage>()
|
||||
private let portraitFillCache = NSCache<NSString, UIImage>()
|
||||
private let landscapeFillCache = NSCache<NSString, UIImage>()
|
||||
private let horCache = NSCache<NSString, UIImage>()
|
||||
private var loadingTasks: [String: (UIImage?) -> Void] = [:]
|
||||
private var activeTokens: [String: CancellationToken] = [:]
|
||||
private let archives = NSCache<NSString, Archive>()
|
||||
private var archives = NSCache<NSString, Archive>()
|
||||
|
||||
init() {
|
||||
// 128 MiB
|
||||
cache.totalCostLimit = 128 * 1024 * 1024
|
||||
portraitFitCache.totalCostLimit = 128 * 1024 * 1024
|
||||
landscapeFitCache.totalCostLimit = 128 * 1024 * 1024
|
||||
portraitFillCache.totalCostLimit = 128 * 1024 * 1024
|
||||
landscapeFillCache.totalCostLimit = 128 * 1024 * 1024
|
||||
horCache.totalCostLimit = 128 * 1024 * 1024
|
||||
// The reason archives are cached and have a strict limit of four, is because Archive supposedly opens a posix file pointer and keeps it until the object is destroyed.
|
||||
archives.countLimit = 4
|
||||
}
|
||||
@@ -1573,6 +1589,7 @@ class ImageLoader {
|
||||
loadImage(archiveURL: archiveURL, filename: filename, scaling: scaling, screenSize: screenSize, completion: nil)
|
||||
}
|
||||
|
||||
// FIXME: So scrolling is not really handled well in terms of caching. This is because it should have a separate cache since it uses a different scaling (Fill instead of Fit).
|
||||
func loadImage(
|
||||
archiveURL: URL, filename: String,
|
||||
scaling: UIView.ContentMode,
|
||||
@@ -1582,28 +1599,23 @@ class ImageLoader {
|
||||
let rotation: Rotation = screenSize.width > screenSize.height ? .horizontal : .vertical
|
||||
let key = filename
|
||||
|
||||
if scaling == .scaleAspectFit {
|
||||
if rotation == .vertical, let cached = portraitFitCache.object(forKey: key as NSString) {
|
||||
completion?(cached); return
|
||||
if rotation == .vertical, let cached = cache.object(forKey: key as NSString) {
|
||||
completion?(cached); return
|
||||
}
|
||||
if rotation == .horizontal, let cached = horCache.object(forKey: key as NSString) {
|
||||
completion?(cached); return
|
||||
}
|
||||
|
||||
if let existing = activeTokens[key], !existing.isCancelled {
|
||||
if let completion = completion {
|
||||
loadingTasks[key] = completion
|
||||
}
|
||||
if rotation == .horizontal, let cached = landscapeFitCache.object(forKey: key as NSString) {
|
||||
completion?(cached); return
|
||||
}
|
||||
} else if scaling == .scaleAspectFill {
|
||||
if rotation == .vertical, let cached = portraitFillCache.object(forKey: key as NSString) {
|
||||
completion?(cached); return
|
||||
}
|
||||
if rotation == .horizontal, let cached = landscapeFillCache.object(forKey: key as NSString) {
|
||||
completion?(cached); return
|
||||
}
|
||||
} else { assertionFailure() }
|
||||
return
|
||||
}
|
||||
|
||||
if let completion = completion {
|
||||
loadingTasks[key] = completion
|
||||
}
|
||||
if let existing = activeTokens[key], !existing.isCancelled {
|
||||
return
|
||||
}
|
||||
|
||||
let token = CancellationToken()
|
||||
activeTokens[key] = token
|
||||
@@ -1611,102 +1623,72 @@ class ImageLoader {
|
||||
queue.async { [weak self] in
|
||||
guard let self = self else { return }
|
||||
|
||||
func finish() {
|
||||
func finish(_ image: UIImage?) {
|
||||
DispatchQueue.main.async {
|
||||
guard self.activeTokens[key] === token else { return }
|
||||
self.activeTokens.removeValue(forKey: key)
|
||||
self.loadingTasks.removeValue(forKey: key)
|
||||
}
|
||||
}
|
||||
func setImage(_ image: UIImage) {
|
||||
DispatchQueue.main.async {
|
||||
if let callback = self.loadingTasks[key] {
|
||||
callback(image)
|
||||
if let image = image {
|
||||
if let callback = self.loadingTasks.removeValue(forKey: key) {
|
||||
callback(image)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if token.isCancelled { finish(); return }
|
||||
if token.isCancelled { finish(nil); return }
|
||||
|
||||
func extractData() -> Data {
|
||||
var extractedData = Data()
|
||||
if self.archives.object(forKey: archiveURL.lastPathComponent as NSString) == nil {
|
||||
let archive = try! Archive(url: archiveURL, accessMode: .read)
|
||||
self.archives.setObject(archive, forKey: archiveURL.lastPathComponent as NSString)
|
||||
}
|
||||
let archive = self.archives.object(forKey: archiveURL.lastPathComponent as NSString)!
|
||||
_ = try! archive.extract(archive[filename]!) { data in
|
||||
extractedData.append(data)
|
||||
}
|
||||
return extractedData
|
||||
var extractedData = Data()
|
||||
if self.archives.object(forKey: archiveURL.lastPathComponent as NSString) == nil {
|
||||
let archive = try! Archive(url: archiveURL, accessMode: .read)
|
||||
self.archives.setObject(archive, forKey: archiveURL.lastPathComponent as NSString)
|
||||
}
|
||||
let archive = self.archives.object(forKey: archiveURL.lastPathComponent as NSString)!
|
||||
_ = try! archive.extract(archive[filename]!) { data in
|
||||
extractedData.append(data)
|
||||
}
|
||||
|
||||
if token.isCancelled { finish(); return }
|
||||
if token.isCancelled { finish(nil); return }
|
||||
|
||||
guard let image = cache.object(forKey: key as NSString) ?? UIImage(data: extractData()) else {
|
||||
finish()
|
||||
guard var image = UIImage(data: extractedData) else {
|
||||
finish(nil)
|
||||
return
|
||||
}
|
||||
|
||||
self.cache.setObject(image, forKey: filename as NSString, cost: imageByteSize(image)!)
|
||||
if token.isCancelled { finish(nil); return }
|
||||
|
||||
if token.isCancelled { finish(); return }
|
||||
// If you turn pages fast, completion will not be nil and as such only the needed scaled image should be prepared
|
||||
if completion == nil || rotation == .vertical {
|
||||
let vertical = CGSize(
|
||||
width: min(screenSize.width, screenSize.height),
|
||||
height: max(screenSize.height, screenSize.width)
|
||||
)
|
||||
let vScaledSize = aspectSize(for: image.size, in: vertical, scaling: scaling)
|
||||
let vScaleImage = resizeImage(image, to: vScaledSize)
|
||||
if let cost = imageByteSize(vScaleImage) {
|
||||
self.cache.setObject(vScaleImage, forKey: filename as NSString, cost: cost)
|
||||
}
|
||||
if rotation == .vertical { image = vScaleImage }
|
||||
}
|
||||
|
||||
let scaledImage = scaleAndCacheImage(
|
||||
image: image,
|
||||
orientation: rotation,
|
||||
screenSize: screenSize,
|
||||
scaling: scaling,
|
||||
filename: key,
|
||||
)
|
||||
if token.isCancelled { finish(nil); return }
|
||||
|
||||
if token.isCancelled { finish(); return }
|
||||
if completion == nil || rotation == .horizontal {
|
||||
let horizontal = CGSize(
|
||||
width: max(screenSize.width, screenSize.height),
|
||||
height: min(screenSize.height, screenSize.width)
|
||||
)
|
||||
let hScaledSize = aspectSize(for: image.size, in: horizontal, scaling: scaling)
|
||||
let hScaleImage = resizeImage(image, to: hScaledSize)
|
||||
if let cost = imageByteSize(hScaleImage) {
|
||||
self.horCache.setObject(hScaleImage, forKey: filename as NSString, cost: cost)
|
||||
}
|
||||
if rotation == .horizontal { image = hScaleImage }
|
||||
}
|
||||
|
||||
setImage(scaledImage)
|
||||
|
||||
let otherOrientation: Rotation = rotation == .vertical ? .horizontal : .vertical
|
||||
|
||||
_ = scaleAndCacheImage(
|
||||
image: image,
|
||||
orientation: otherOrientation,
|
||||
screenSize: screenSize,
|
||||
scaling: scaling,
|
||||
filename: key,
|
||||
)
|
||||
finish()
|
||||
finish(image)
|
||||
}
|
||||
}
|
||||
|
||||
func scaleAndCacheImage(image: UIImage, orientation: Rotation, screenSize: CGSize, scaling: UIView.ContentMode, filename: String) -> UIImage {
|
||||
let resizeTo: CGSize = if orientation == .vertical {
|
||||
CGSize(
|
||||
width: min(screenSize.width, screenSize.height),
|
||||
height: max(screenSize.height, screenSize.width)
|
||||
)
|
||||
} else {
|
||||
CGSize(
|
||||
width: max(screenSize.width, screenSize.height),
|
||||
height: min(screenSize.height, screenSize.width)
|
||||
)
|
||||
}
|
||||
let scaledSize = aspectSize(for: image.size, in: resizeTo, scaling: scaling)
|
||||
let scaledImage = resizeImage(image, to: scaledSize)
|
||||
let cost = imageByteSize(scaledImage)!
|
||||
switch ((orientation, scaling)) {
|
||||
case (.vertical, .scaleAspectFit):
|
||||
self.portraitFitCache.setObject(scaledImage, forKey: filename as NSString, cost: cost)
|
||||
case (.vertical, .scaleAspectFill):
|
||||
self.portraitFillCache.setObject(scaledImage, forKey: filename as NSString, cost: cost)
|
||||
case (.horizontal, .scaleAspectFit):
|
||||
self.landscapeFitCache.setObject(scaledImage, forKey: filename as NSString, cost: cost)
|
||||
case (.horizontal, .scaleAspectFill):
|
||||
self.landscapeFillCache.setObject(scaledImage, forKey: filename as NSString, cost: cost)
|
||||
default:
|
||||
assertionFailure()
|
||||
}
|
||||
return scaledImage
|
||||
}
|
||||
|
||||
func setActiveWindow(_ paths: [String]) {
|
||||
let desired = Set(paths)
|
||||
for (key, token) in activeTokens where !desired.contains(key) {
|
||||
@@ -1714,6 +1696,10 @@ class ImageLoader {
|
||||
loadingTasks[key] = nil
|
||||
}
|
||||
}
|
||||
|
||||
func imageIfLoaded(at path: String) -> UIImage? {
|
||||
return cache.object(forKey: path as NSString)
|
||||
}
|
||||
}
|
||||
|
||||
func imageByteSize(_ image: UIImage) -> Int? {
|
||||
|
||||
Reference in New Issue
Block a user