mirror of
https://github.com/SideStore/SideStore.git
synced 2026-02-12 08:13:26 +01:00
[AltStore] Loads images remotely rather than including them in app bundle
This commit is contained in:
219
Pods/Nuke/Sources/ImageDecoding.swift
generated
Normal file
219
Pods/Nuke/Sources/ImageDecoding.swift
generated
Normal file
@@ -0,0 +1,219 @@
|
||||
// The MIT License (MIT)
|
||||
//
|
||||
// Copyright (c) 2015-2019 Alexander Grebenyuk (github.com/kean).
|
||||
|
||||
#if !os(macOS)
|
||||
import UIKit
|
||||
#else
|
||||
import Cocoa
|
||||
#endif
|
||||
|
||||
#if os(watchOS)
|
||||
import WatchKit
|
||||
#endif
|
||||
|
||||
// MARK: - ImageDecoding
|
||||
|
||||
/// Decodes image data.
|
||||
public protocol ImageDecoding {
|
||||
/// Produces an image from the image data. A decoder is a one-shot object
|
||||
/// created for a single image decoding session. If image pipeline has
|
||||
/// progressive decoding enabled, the `decode(data:isFinal:)` method gets
|
||||
/// called each time the data buffer has new data available. The decoder may
|
||||
/// decide whether or not to produce a new image based on the previous scans.
|
||||
func decode(data: Data, isFinal: Bool) -> Image?
|
||||
}
|
||||
|
||||
// An image decoder that uses native APIs. Supports progressive decoding.
|
||||
// The decoder is stateful.
|
||||
public final class ImageDecoder: ImageDecoding {
|
||||
// `nil` if decoder hasn't detected whether progressive decoding is enabled.
|
||||
private(set) internal var isProgressive: Bool?
|
||||
// Number of scans that the decoder has found so far. The last scan might be
|
||||
// incomplete at this point.
|
||||
private(set) internal var numberOfScans = 0
|
||||
private var lastStartOfScan: Int = 0 // Index of the last Start of Scan that we found
|
||||
private var scannedIndex: Int = -1 // Index at which previous scan was finished
|
||||
|
||||
public init() { }
|
||||
|
||||
public func decode(data: Data, isFinal: Bool) -> Image? {
|
||||
let format = ImageFormat.format(for: data)
|
||||
|
||||
guard !isFinal else { // Just decode the data.
|
||||
let image = _decode(data)
|
||||
if ImagePipeline.Configuration.isAnimatedImageDataEnabled, case .gif? = format { // Keep original data around in case of GIF
|
||||
image?.animatedImageData = data
|
||||
}
|
||||
return image
|
||||
}
|
||||
|
||||
// Determined (if we haven't yet) whether the image supports progressive
|
||||
// decoding or not (only proressive JPEG is allowed for now, but you can
|
||||
// add support for other formats by implementing your own decoder).
|
||||
isProgressive = isProgressive ?? format?.isProgressive
|
||||
guard isProgressive == true else { return nil }
|
||||
|
||||
// Check if there is more data to scan.
|
||||
guard (scannedIndex + 1) < data.count else { return nil }
|
||||
|
||||
// Start scaning from the where we left off previous time.
|
||||
var index = (scannedIndex + 1)
|
||||
var numberOfScans = self.numberOfScans
|
||||
while index < (data.count - 1) {
|
||||
scannedIndex = index
|
||||
// 0xFF, 0xDA - Start Of Scan
|
||||
if data[index] == 0xFF, data[index+1] == 0xDA {
|
||||
lastStartOfScan = index
|
||||
numberOfScans += 1
|
||||
}
|
||||
index += 1
|
||||
}
|
||||
|
||||
// Found more scans this the previous time
|
||||
guard numberOfScans > self.numberOfScans else { return nil }
|
||||
self.numberOfScans = numberOfScans
|
||||
|
||||
// `> 1` checks that we've received a first scan (SOS) and then received
|
||||
// and also received a second scan (SOS). This way we know that we have
|
||||
// at least one full scan available.
|
||||
return (numberOfScans > 1 && lastStartOfScan > 0) ? _decode(data[0..<lastStartOfScan]) : nil
|
||||
}
|
||||
}
|
||||
|
||||
// Image initializers are documented as fully-thread safe:
|
||||
//
|
||||
// > The immutable nature of image objects also means that they are safe
|
||||
// to create and use from any thread.
|
||||
//
|
||||
// However, there are some versions of iOS which violated this. The
|
||||
// `UIImage` is supposably fully thread safe again starting with iOS 10.
|
||||
//
|
||||
// The `queue.sync` call below prevents the majority of the potential
|
||||
// crashes that could happen on the previous versions of iOS.
|
||||
//
|
||||
// See also https://github.com/AFNetworking/AFNetworking/issues/2572
|
||||
private let _queue = DispatchQueue(label: "com.github.kean.Nuke.DataDecoder")
|
||||
|
||||
internal func _decode(_ data: Data) -> Image? {
|
||||
return _queue.sync {
|
||||
#if os(macOS)
|
||||
return NSImage(data: data)
|
||||
#else
|
||||
#if os(iOS) || os(tvOS)
|
||||
let scale = UIScreen.main.scale
|
||||
#else
|
||||
let scale = WKInterfaceDevice.current().screenScale
|
||||
#endif
|
||||
return UIImage(data: data, scale: scale)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - ImageDecoderRegistry
|
||||
|
||||
/// A register of image codecs (only decoding).
|
||||
public final class ImageDecoderRegistry {
|
||||
/// A shared registry.
|
||||
public static let shared = ImageDecoderRegistry()
|
||||
|
||||
private var matches = [(ImageDecodingContext) -> ImageDecoding?]()
|
||||
|
||||
/// Returns a decoder which matches the given context.
|
||||
public func decoder(for context: ImageDecodingContext) -> ImageDecoding {
|
||||
for match in matches {
|
||||
if let decoder = match(context) {
|
||||
return decoder
|
||||
}
|
||||
}
|
||||
return ImageDecoder() // Return default decoder if couldn't find a custom one.
|
||||
}
|
||||
|
||||
/// Registers a decoder to be used in a given decoding context. The closure
|
||||
/// is going to be executed before all other already registered closures.
|
||||
public func register(_ match: @escaping (ImageDecodingContext) -> ImageDecoding?) {
|
||||
matches.insert(match, at: 0)
|
||||
}
|
||||
|
||||
func clear() {
|
||||
matches = []
|
||||
}
|
||||
}
|
||||
|
||||
/// Image decoding context used when selecting which decoder to use.
|
||||
public struct ImageDecodingContext {
|
||||
public let request: ImageRequest
|
||||
internal let urlResponse: URLResponse?
|
||||
public let data: Data
|
||||
}
|
||||
|
||||
// MARK: - Image Formats
|
||||
|
||||
enum ImageFormat: Equatable {
|
||||
/// `isProgressive` is nil if we determined that it's a jpeg, but we don't
|
||||
/// know if it is progressive or baseline yet.
|
||||
case jpeg(isProgressive: Bool?)
|
||||
case png
|
||||
case gif
|
||||
|
||||
// Returns `nil` if not enough data.
|
||||
static func format(for data: Data) -> ImageFormat? {
|
||||
// JPEG magic numbers https://en.wikipedia.org/wiki/JPEG
|
||||
if _match(data, [0xFF, 0xD8, 0xFF]) {
|
||||
var index = 3 // start scanning right after magic numbers
|
||||
while index < (data.count - 1) {
|
||||
// A example of first few bytes of progressive jpeg image:
|
||||
// FF D8 FF E0 00 10 4A 46 49 46 00 01 01 00 00 48 00 ...
|
||||
//
|
||||
// 0xFF, 0xC0 - Start Of Frame (baseline DCT)
|
||||
// 0xFF, 0xC2 - Start Of Frame (progressive DCT)
|
||||
// https://en.wikipedia.org/wiki/JPEG
|
||||
if data[index] == 0xFF {
|
||||
if data[index+1] == 0xC2 { return .jpeg(isProgressive: true) } // progressive
|
||||
if data[index+1] == 0xC0 { return .jpeg(isProgressive: false) } // baseline
|
||||
}
|
||||
index += 1
|
||||
}
|
||||
// It's a jpeg but we don't know if progressive or not yet.
|
||||
return .jpeg(isProgressive: nil)
|
||||
}
|
||||
|
||||
// GIF magic numbers https://en.wikipedia.org/wiki/GIF
|
||||
if _match(data, [0x47, 0x49, 0x46]) {
|
||||
return .gif
|
||||
}
|
||||
|
||||
// PNG Magic numbers https://en.wikipedia.org/wiki/Portable_Network_Graphics
|
||||
if _match(data, [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]) {
|
||||
return .png
|
||||
}
|
||||
|
||||
// Either not enough data, or we just don't know this format yet.
|
||||
return nil
|
||||
}
|
||||
|
||||
var isProgressive: Bool? {
|
||||
if case let .jpeg(isProgressive) = self { return isProgressive }
|
||||
return false
|
||||
}
|
||||
|
||||
private static func _match(_ data: Data, _ numbers: [UInt8]) -> Bool {
|
||||
guard data.count >= numbers.count else { return false }
|
||||
return !zip(numbers.indices, numbers).contains { (index, number) in
|
||||
data[index] != number
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Animated Images
|
||||
|
||||
private var _animatedImageDataAK = "Nuke.AnimatedImageData.AssociatedKey"
|
||||
|
||||
extension Image {
|
||||
// Animated image data. Only not `nil` when image data actually contains
|
||||
// an animated image.
|
||||
public var animatedImageData: Data? {
|
||||
get { return objc_getAssociatedObject(self, &_animatedImageDataAK) as? Data }
|
||||
set { objc_setAssociatedObject(self, &_animatedImageDataAK, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user