├── .gitignore ├── BBWebImage.podspec ├── BBWebImage.xcworkspace ├── contents.xcworkspacedata └── xcshareddata │ └── IDEWorkspaceChecks.plist ├── BBWebImage ├── BBWebImage.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── xcshareddata │ │ └── xcschemes │ │ └── BBWebImageTests.xcscheme ├── BBWebImage │ ├── BBWebImage.h │ ├── Core │ │ ├── BBAnimatedImage.swift │ │ ├── BBAnimatedImageView.swift │ │ ├── BBDiskCache.swift │ │ ├── BBDiskStorage.swift │ │ ├── BBDispatchQueuePool.swift │ │ ├── BBImageCache.swift │ │ ├── BBImageCoder.swift │ │ ├── BBImageDownloadOperation.swift │ │ ├── BBImageDownloadOperationQueue.swift │ │ ├── BBImageDownloader.swift │ │ ├── BBMemoryCache.swift │ │ ├── BBWeakProxy.swift │ │ ├── BBWebCache.swift │ │ ├── BBWebCacheResource.swift │ │ ├── BBWebImageEditor.swift │ │ ├── BBWebImageGIFCoder.swift │ │ ├── BBWebImageImageIOCoder.swift │ │ └── BBWebImageManager.swift │ ├── Extensions │ │ ├── CALayer+BBWebCache.swift │ │ ├── Data+ImageFormat.swift │ │ ├── DispatchQueue+Safe.swift │ │ ├── MKAnnotationView+BBWebCache.swift │ │ ├── String+MD5.swift │ │ ├── UIButton+BBWebCache.swift │ │ ├── UIDevice+BBAdd.swift │ │ ├── UIImage+ImageFormat.swift │ │ └── UIImageView+BBWebCache.swift │ ├── Filter │ │ ├── BBCILookup.cikernel │ │ └── BBCILookupFilter.swift │ └── Info.plist └── BBWebImageTests │ ├── BBAnimatedImageTests.swift │ ├── BBImageCoderManagerTests.swift │ ├── BBLRUImageCacheTests.swift │ ├── BBMergeRequestImageDownloaderTests.swift │ ├── BBWebImageGIFCoderTests.swift │ ├── BBWebImageManagerTests.swift │ ├── Info.plist │ ├── TestImageDownloadOperation.swift │ ├── TestImageDownloadTask.swift │ ├── mew_baseline.jpg │ └── mew_baseline.png ├── BBWebImageDemo ├── BBWebImageDemo.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── BBWebImageDemo │ ├── AppDelegate.swift │ ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── Contents.json │ ├── BBCILookupTestFilter.swift │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── GIFWallCell.swift │ ├── GIFWallVC.swift │ ├── ImageURLProvider.swift │ ├── ImageWallCell.swift │ ├── ImageWallVC.swift │ ├── Info.plist │ ├── MainMenuVC.swift │ ├── Rotating_earth.gif │ ├── TestFilterVC.swift │ ├── TestGIFVC.swift │ ├── TestVC.swift │ ├── large_leaves_70mp.jpg │ ├── placeholder.png │ ├── sunflower.jpg │ └── test_lookup.png ├── CompareImageLib ├── CompareImageLib.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── CompareImageLib.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── CompareImageLib │ ├── AppDelegate.swift │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ └── Contents.json │ ├── BBAnimatedImageWallCell.swift │ ├── BBAnimatedImageWallVC.swift │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── ImageWallCell.swift │ ├── ImageWallVC.swift │ ├── Info.plist │ ├── KiAnimatedImageWallCell.swift │ ├── KiAnimatedImageWallVC.swift │ ├── MainMenuVC.swift │ ├── SDAnimatedImageWallCell.swift │ ├── SDAnimatedImageWallVC.swift │ ├── TestCacheVC.swift │ ├── TestImages │ │ ├── test_1.jpg │ │ ├── test_10.jpg │ │ ├── test_100.jpg │ │ ├── test_100_size_64.jpg │ │ ├── test_10_size_64.jpg │ │ ├── test_11.jpg │ │ ├── test_11_size_64.jpg │ │ ├── test_12.jpg │ │ ├── test_12_size_64.jpg │ │ ├── test_13.jpg │ │ ├── test_13_size_64.jpg │ │ ├── test_14.jpg │ │ ├── test_14_size_64.jpg │ │ ├── test_15.jpg │ │ ├── test_15_size_64.jpg │ │ ├── test_16.jpg │ │ ├── test_16_size_64.jpg │ │ ├── test_17.jpg │ │ ├── test_17_size_64.jpg │ │ ├── test_18.jpg │ │ ├── test_18_size_64.jpg │ │ ├── test_19.jpg │ │ ├── test_19_size_64.jpg │ │ ├── test_1_size_64.jpg │ │ ├── test_2.jpg │ │ ├── test_20.jpg │ │ ├── test_20_size_64.jpg │ │ ├── test_21.jpg │ │ ├── test_21_size_64.jpg │ │ ├── test_22.jpg │ │ ├── test_22_size_64.jpg │ │ ├── test_23.jpg │ │ ├── test_23_size_64.jpg │ │ ├── test_24.jpg │ │ ├── test_24_size_64.jpg │ │ ├── test_25.jpg │ │ ├── test_25_size_64.jpg │ │ ├── test_26.jpg │ │ ├── test_26_size_64.jpg │ │ ├── test_27.jpg │ │ ├── test_27_size_64.jpg │ │ ├── test_28.jpg │ │ ├── test_28_size_64.jpg │ │ ├── test_29.jpg │ │ ├── test_29_size_64.jpg │ │ ├── test_2_size_64.jpg │ │ ├── test_3.jpg │ │ ├── test_30.jpg │ │ ├── test_30_size_64.jpg │ │ ├── test_31.jpg │ │ ├── test_31_size_64.jpg │ │ ├── test_32.jpg │ │ ├── test_32_size_64.jpg │ │ ├── test_33.jpg │ │ ├── test_33_size_64.jpg │ │ ├── test_34.jpg │ │ ├── test_34_size_64.jpg │ │ ├── test_35.jpg │ │ ├── test_35_size_64.jpg │ │ ├── test_36.jpg │ │ ├── test_36_size_64.jpg │ │ ├── test_37.jpg │ │ ├── test_37_size_64.jpg │ │ ├── test_38.jpg │ │ ├── test_38_size_64.jpg │ │ ├── test_39.jpg │ │ ├── test_39_size_64.jpg │ │ ├── test_3_size_64.jpg │ │ ├── test_4.jpg │ │ ├── test_40.jpg │ │ ├── test_40_size_64.jpg │ │ ├── test_41.jpg │ │ ├── test_41_size_64.jpg │ │ ├── test_42.jpg │ │ ├── test_42_size_64.jpg │ │ ├── test_43.jpg │ │ ├── test_43_size_64.jpg │ │ ├── test_44.jpg │ │ ├── test_44_size_64.jpg │ │ ├── test_45.jpg │ │ ├── test_45_size_64.jpg │ │ ├── test_46.jpg │ │ ├── test_46_size_64.jpg │ │ ├── test_47.jpg │ │ ├── test_47_size_64.jpg │ │ ├── test_48.jpg │ │ ├── test_48_size_64.jpg │ │ ├── test_49.jpg │ │ ├── test_49_size_64.jpg │ │ ├── test_4_size_64.jpg │ │ ├── test_5.jpg │ │ ├── test_50.jpg │ │ ├── test_50_size_64.jpg │ │ ├── test_51.jpg │ │ ├── test_51_size_64.jpg │ │ ├── test_52.jpg │ │ ├── test_52_size_64.jpg │ │ ├── test_53.jpg │ │ ├── test_53_size_64.jpg │ │ ├── test_54.jpg │ │ ├── test_54_size_64.jpg │ │ ├── test_55.jpg │ │ ├── test_55_size_64.jpg │ │ ├── test_56.jpg │ │ ├── test_56_size_64.jpg │ │ ├── test_57.jpg │ │ ├── test_57_size_64.jpg │ │ ├── test_58.jpg │ │ ├── test_58_size_64.jpg │ │ ├── test_59.jpg │ │ ├── test_59_size_64.jpg │ │ ├── test_5_size_64.jpg │ │ ├── test_6.jpg │ │ ├── test_60.jpg │ │ ├── test_60_size_64.jpg │ │ ├── test_61.jpg │ │ ├── test_61_size_64.jpg │ │ ├── test_62.jpg │ │ ├── test_62_size_64.jpg │ │ ├── test_63.jpg │ │ ├── test_63_size_64.jpg │ │ ├── test_64.jpg │ │ ├── test_64_size_64.jpg │ │ ├── test_65.jpg │ │ ├── test_65_size_64.jpg │ │ ├── test_66.jpg │ │ ├── test_66_size_64.jpg │ │ ├── test_67.jpg │ │ ├── test_67_size_64.jpg │ │ ├── test_68.jpg │ │ ├── test_68_size_64.jpg │ │ ├── test_69.jpg │ │ ├── test_69_size_64.jpg │ │ ├── test_6_size_64.jpg │ │ ├── test_7.jpg │ │ ├── test_70.jpg │ │ ├── test_70_size_64.jpg │ │ ├── test_71.jpg │ │ ├── test_71_size_64.jpg │ │ ├── test_72.jpg │ │ ├── test_72_size_64.jpg │ │ ├── test_73.jpg │ │ ├── test_73_size_64.jpg │ │ ├── test_74.jpg │ │ ├── test_74_size_64.jpg │ │ ├── test_75.jpg │ │ ├── test_75_size_64.jpg │ │ ├── test_76.jpg │ │ ├── test_76_size_64.jpg │ │ ├── test_77.jpg │ │ ├── test_77_size_64.jpg │ │ ├── test_78.jpg │ │ ├── test_78_size_64.jpg │ │ ├── test_79.jpg │ │ ├── test_79_size_64.jpg │ │ ├── test_7_size_64.jpg │ │ ├── test_8.jpg │ │ ├── test_80.jpg │ │ ├── test_80_size_64.jpg │ │ ├── test_81.jpg │ │ ├── test_81_size_64.jpg │ │ ├── test_82.jpg │ │ ├── test_82_size_64.jpg │ │ ├── test_83.jpg │ │ ├── test_83_size_64.jpg │ │ ├── test_84.jpg │ │ ├── test_84_size_64.jpg │ │ ├── test_85.jpg │ │ ├── test_85_size_64.jpg │ │ ├── test_86.jpg │ │ ├── test_86_size_64.jpg │ │ ├── test_87.jpg │ │ ├── test_87_size_64.jpg │ │ ├── test_88.jpg │ │ ├── test_88_size_64.jpg │ │ ├── test_89.jpg │ │ ├── test_89_size_64.jpg │ │ ├── test_8_size_64.jpg │ │ ├── test_9.jpg │ │ ├── test_90.jpg │ │ ├── test_90_size_64.jpg │ │ ├── test_91.jpg │ │ ├── test_91_size_64.jpg │ │ ├── test_92.jpg │ │ ├── test_92_size_64.jpg │ │ ├── test_93.jpg │ │ ├── test_93_size_64.jpg │ │ ├── test_94.jpg │ │ ├── test_94_size_64.jpg │ │ ├── test_95.jpg │ │ ├── test_95_size_64.jpg │ │ ├── test_96.jpg │ │ ├── test_96_size_64.jpg │ │ ├── test_97.jpg │ │ ├── test_97_size_64.jpg │ │ ├── test_98.jpg │ │ ├── test_98_size_64.jpg │ │ ├── test_99.jpg │ │ ├── test_99_size_64.jpg │ │ └── test_9_size_64.jpg │ ├── YYAnimatedImageWallCell.swift │ └── YYAnimatedImageWallVC.swift ├── Podfile └── Podfile.lock ├── LICENSE ├── README.md └── README_resources ├── CompareImageLib.key ├── CompareImageLib.numbers ├── architecture.png ├── compare_diskCache.png ├── compare_gif_CPU_memory.png ├── compare_memoryCache.png ├── edit_common.gif ├── edit_filter.gif └── original_image.gif /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xccheckout 23 | *.xcscmblueprint 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | *.ipa 28 | *.dSYM.zip 29 | *.dSYM 30 | 31 | ## Playgrounds 32 | timeline.xctimeline 33 | playground.xcworkspace 34 | 35 | # Swift Package Manager 36 | # 37 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 38 | # Packages/ 39 | # Package.pins 40 | # Package.resolved 41 | .build/ 42 | 43 | # CocoaPods 44 | 45 | Pods/ 46 | 47 | # Carthage 48 | # 49 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 50 | # Carthage/Checkouts 51 | 52 | Carthage/Build 53 | 54 | # fastlane 55 | # 56 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 57 | # screenshots whenever they are needed. 58 | # For more information about the recommended setup visit: 59 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 60 | 61 | fastlane/report.xml 62 | fastlane/Preview.html 63 | fastlane/screenshots/**/*.png 64 | fastlane/test_output 65 | 66 | # Code Injection 67 | # 68 | # After new code Injection tools there's a generated folder /iOSInjectionProject 69 | # https://github.com/johnno1962/injectionforxcode 70 | 71 | iOSInjectionProject/ 72 | 73 | # macOS 74 | 75 | .DS_Store 76 | -------------------------------------------------------------------------------- /BBWebImage.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = 'BBWebImage' 3 | s.version = '1.1.3' 4 | s.summary = 'A high performance Swift library for downloading, caching and editing web images asynchronously.' 5 | 6 | s.description = <<-DESC 7 | View extensions for `UIImageView`, `UIButton`, `MKAnnotationView` and `CALayer` to set image from URL. 8 | Asynchronous image downloader. 9 | Asynchronous memory + file + SQLite image cache with least recently used algorithm. 10 | Asynchronous image decompressing. 11 | Asynchronous image editing without modifying original image disk data. 12 | Animated image smart decoding, decompressing, editing and caching. 13 | Independent image cache, downloader, coder and editor for separate use. 14 | Customized image cache, downloader and coder. 15 | High performance. 16 | DESC 17 | 18 | s.homepage = 'https://github.com/Silence-GitHub/BBWebImage' 19 | 20 | s.license = { :type => 'MIT', :file => 'LICENSE' } 21 | 22 | s.author = { 'Kaibo Lu' => 'lukaibolkb@gmail.com' } 23 | 24 | s.platform = :ios, '8.0' 25 | 26 | s.swift_version = '5.0' 27 | 28 | s.source = { :git => 'https://github.com/Silence-GitHub/BBWebImage.git', :tag => s.version } 29 | 30 | s.requires_arc = true 31 | 32 | s.default_subspec = 'Core' 33 | 34 | s.subspec 'Core' do |core| 35 | core.source_files = 'BBWebImage/BBWebImage/BBWebImage.h', 'BBWebImage/BBWebImage/**/*.swift' 36 | core.exclude_files = 'BBWebImage/BBWebImage/Extensions/MKAnnotationView+BBWebCache.swift', 'BBWebImage/BBWebImage/Filter/*' 37 | end 38 | 39 | s.subspec 'MapKit' do |mk| 40 | mk.source_files = 'BBWebImage/BBWebImage/Extensions/MKAnnotationView+BBWebCache.swift' 41 | mk.dependency 'BBWebImage/Core' 42 | end 43 | 44 | s.subspec 'Filter' do |filter| 45 | filter.source_files = 'BBWebImage/BBWebImage/Filter' 46 | filter.resources = 'BBWebImage/BBWebImage/**/*.cikernel' 47 | filter.dependency 'BBWebImage/Core' 48 | end 49 | 50 | end 51 | -------------------------------------------------------------------------------- /BBWebImage.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /BBWebImage.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /BBWebImage/BBWebImage.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /BBWebImage/BBWebImage.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /BBWebImage/BBWebImage.xcodeproj/xcshareddata/xcschemes/BBWebImageTests.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 14 | 15 | 17 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 39 | 40 | 41 | 42 | 48 | 49 | 51 | 52 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /BBWebImage/BBWebImage/BBWebImage.h: -------------------------------------------------------------------------------- 1 | // 2 | // BBWebImage.h 3 | // BBWebImage 4 | // 5 | // Created by Kaibo Lu on 2018/10/3. 6 | // Copyright © 2018年 Kaibo Lu. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for BBWebImage. 12 | FOUNDATION_EXPORT double BBWebImageVersionNumber; 13 | 14 | //! Project version string for BBWebImage. 15 | FOUNDATION_EXPORT const unsigned char BBWebImageVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /BBWebImage/BBWebImage/Core/BBDiskCache.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BBDiskCache.swift 3 | // BBWebImage 4 | // 5 | // Created by Kaibo Lu on 2018/10/29. 6 | // Copyright © 2018年 Kaibo Lu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | /// BBDiskCache is a key-value disk cache using least recently used algorithm 12 | public class BBDiskCache { 13 | private let storage: BBDiskStorage 14 | private let sizeThreshold: Int 15 | private let queue: BBDispatchQueuePool 16 | private var costLimit: Int 17 | private var countLimit: Int 18 | private var ageLimit: TimeInterval 19 | 20 | /// Crreates a BBDiskCache object 21 | /// 22 | /// - Parameters: 23 | /// - path: directory storing image data 24 | /// - threshold: threshold specifying image data is store in sqlite (data.count <= threshold) or file (data.count > threshold) 25 | public init?(path: String, sizeThreshold threshold: Int) { 26 | if let currentStorage = BBDiskStorage(path: path) { 27 | storage = currentStorage 28 | } else { 29 | return nil 30 | } 31 | sizeThreshold = threshold 32 | queue = BBDispatchQueuePool.utility 33 | costLimit = .max 34 | countLimit = .max 35 | ageLimit = .greatestFiniteMagnitude 36 | trimRecursively() 37 | } 38 | 39 | /// Gets data with key synchronously 40 | /// 41 | /// - Parameter key: cache key 42 | /// - Returns: data in disk, or nil if no data found 43 | public func data(forKey key: String) -> Data? { 44 | return storage.data(forKey: key) 45 | } 46 | 47 | /// Gets data with key asynchronously 48 | /// 49 | /// - Parameters: 50 | /// - key: cache key 51 | /// - completion: a closure called when querying is finished 52 | public func data(forKey key: String, completion: @escaping (Data?) -> Void) { 53 | queue.async { [weak self] in 54 | guard let self = self else { return } 55 | completion(self.data(forKey: key)) 56 | } 57 | } 58 | 59 | /// Checks whether data is in the disk cache. 60 | /// This method checks cache synchronously. 61 | /// 62 | /// - Parameters: 63 | /// - key: cache key 64 | /// - Returns: true if data is in the cache, or false if not 65 | public func dataExists(forKey key: String) -> Bool { 66 | return storage.dataExists(forKey: key) 67 | } 68 | 69 | /// Checks whether data is in the disk cache. 70 | /// This method checks cache asynchronously. 71 | /// 72 | /// - Parameters: 73 | /// - key: cache key 74 | /// - completion: a closure called when checking is finished 75 | public func dataExists(forKey key: String, completion: @escaping BBImageCacheCheckDiskCompletion) { 76 | queue.async { [weak self] in 77 | guard let self = self else { return } 78 | completion(self.dataExists(forKey: key)) 79 | } 80 | } 81 | 82 | /// Stores data with key synchronously 83 | /// 84 | /// - Parameters: 85 | /// - data: data to store 86 | /// - key: cache key 87 | public func store(_ data: Data, forKey key: String) { 88 | storage.store(data, forKey: key, type: (data.count > sizeThreshold ? .file : .sqlite)) 89 | } 90 | 91 | /// Stores data with key asynchronously 92 | /// 93 | /// - Parameters: 94 | /// - data: data to store 95 | /// - key: cache key 96 | /// - completion: a closure called when storing is finished 97 | public func store(_ data: Data, forKey key: String, completion: BBImageCacheStoreCompletion?) { 98 | queue.async { [weak self] in 99 | guard let self = self else { return } 100 | self.store(data, forKey: key) 101 | completion?() 102 | } 103 | } 104 | 105 | /// Stores data with data work closure and key asynchronously 106 | /// 107 | /// - Parameters: 108 | /// - dataWork: a closure called in a background thread, returning data 109 | /// - key: cache key 110 | /// - completion: a closure called when storing is finished 111 | public func store(_ dataWork: @escaping () -> Data?, forKey key: String, completion: BBImageCacheStoreCompletion?) { 112 | queue.async { [weak self] in 113 | guard let self = self else { return } 114 | if let data = dataWork() { 115 | self.store(data, forKey: key) 116 | } 117 | completion?() 118 | } 119 | } 120 | 121 | /// Removes data with key synchronously 122 | /// 123 | /// - Parameter key: cache key 124 | public func removeData(forKey key: String) { 125 | storage.removeData(forKey: key) 126 | } 127 | 128 | /// Removes data with key asynchronously 129 | /// 130 | /// - Parameters: 131 | /// - key: cache key 132 | /// - completion: a closure called when removing is finished 133 | public func removeData(forKey key: String, completion: BBImageCacheRemoveCompletion?) { 134 | queue.async { [weak self] in 135 | guard let self = self else { return } 136 | self.removeData(forKey: key) 137 | completion?() 138 | } 139 | } 140 | 141 | /// Removes all data synchronously 142 | public func clear() { 143 | storage.clear() 144 | } 145 | 146 | /// Removes all data asynchronously 147 | /// 148 | /// - Parameter completion: a closure called when clearing is finished 149 | public func clear(_ completion: BBImageCacheRemoveCompletion?) { 150 | queue.async { [weak self] in 151 | guard let self = self else { return } 152 | self.clear() 153 | completion?() 154 | } 155 | } 156 | 157 | private func trimRecursively() { 158 | DispatchQueue.global(qos: .background).asyncAfter(deadline: .now() + 60) { [weak self] in 159 | guard let self = self else { return } 160 | self.storage.trim(toCost: self.costLimit) 161 | self.storage.trim(toCount: self.countLimit) 162 | self.storage.trim(toAge: self.ageLimit) 163 | self.trimRecursively() 164 | } 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /BBWebImage/BBWebImage/Core/BBDispatchQueuePool.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BBDispatchQueuePool.swift 3 | // BBWebImage 4 | // 5 | // Created by Kaibo Lu on 2018/11/9. 6 | // Copyright © 2018年 Kaibo Lu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | /// BBDispatchQueuePool holds mutiple serial queues. 12 | /// To prevent concurrent queue increasing thread count, use this class to control thread count. 13 | public class BBDispatchQueuePool { 14 | public static let userInteractive = BBDispatchQueuePool(label: "com.Kaibo.BBWebImage.QueuePool.userInteractive", qos: .userInteractive) 15 | public static let userInitiated = BBDispatchQueuePool(label: "com.Kaibo.BBWebImage.QueuePool.userInitiated", qos: .userInitiated) 16 | public static let utility = BBDispatchQueuePool(label: "com.Kaibo.BBWebImage.QueuePool.utility", qos: .utility) 17 | public static let `default` = BBDispatchQueuePool(label: "com.Kaibo.BBWebImage.QueuePool.default", qos: .default) 18 | public static let background = BBDispatchQueuePool(label: "com.Kaibo.BBWebImage.QueuePool.background", qos: .background) 19 | 20 | private let queues: [DispatchQueue] 21 | private var index: Int32 22 | 23 | /// Gets a dispatch queue from pool 24 | public var currentQueue: DispatchQueue { 25 | var currentIndex = OSAtomicIncrement32(&index) 26 | if currentIndex < 0 { currentIndex = -currentIndex } 27 | return queues[Int(currentIndex) % queues.count] 28 | } 29 | 30 | /// Creates a BBDispatchQueuePool object 31 | /// 32 | /// - Parameters: 33 | /// - label: dispatch queue label 34 | /// - qos: quality of service for dispatch queue 35 | /// - queueCount: dispatch queue count 36 | public init(label: String, qos: DispatchQoS, queueCount: Int = 0) { 37 | let count = queueCount > 0 ? queueCount : min(16, max(1, ProcessInfo.processInfo.activeProcessorCount)) 38 | var pool: [DispatchQueue] = [] 39 | for i in 0.. Void) { 51 | currentQueue.async(execute: work) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /BBWebImage/BBWebImage/Core/BBImageDownloadOperationQueue.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BBImageDownloadOperationQueue.swift 3 | // BBWebImage 4 | // 5 | // Created by Kaibo Lu on 2/1/19. 6 | // Copyright © 2019 Kaibo Lu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | private class BBImageDownloadLinkedMapNode { 12 | fileprivate weak var prev: BBImageDownloadLinkedMapNode? 13 | fileprivate weak var next: BBImageDownloadLinkedMapNode? 14 | fileprivate var key: URL 15 | fileprivate var value: BBImageDownloadOperation 16 | 17 | fileprivate init(key: URL, value: BBImageDownloadOperation) { 18 | self.key = key 19 | self.value = value 20 | } 21 | } 22 | 23 | private class BBImageDownloadLinkedMap { 24 | fileprivate var dic: [URL : BBImageDownloadLinkedMapNode] 25 | fileprivate var head: BBImageDownloadLinkedMapNode? 26 | fileprivate var tail: BBImageDownloadLinkedMapNode? 27 | 28 | init() { dic = [:] } 29 | 30 | fileprivate func enqueue(_ node: BBImageDownloadLinkedMapNode) { 31 | dic[node.key] = node 32 | if head == nil { 33 | head = node 34 | tail = node 35 | } else { 36 | tail?.next = node 37 | node.prev = tail 38 | tail = node 39 | } 40 | } 41 | 42 | fileprivate func dequeue() -> BBImageDownloadLinkedMapNode? { 43 | if let node = head { 44 | remove(node) 45 | return node 46 | } 47 | return nil 48 | } 49 | 50 | fileprivate func remove(_ node: BBImageDownloadLinkedMapNode) { 51 | dic[node.key] = nil 52 | node.prev?.next = node.next 53 | node.next?.prev = node.prev 54 | if head === node { head = node.next } 55 | if tail === node { tail = node.prev } 56 | } 57 | } 58 | 59 | class BBImageDownloadOperationQueue { 60 | private let waitingQueue: BBImageDownloadLinkedMap 61 | private let preloadWaitingQueue: BBImageDownloadLinkedMap 62 | var maxRunningCount: Int 63 | private(set) var currentRunningCount: Int 64 | 65 | init() { 66 | waitingQueue = BBImageDownloadLinkedMap() 67 | preloadWaitingQueue = BBImageDownloadLinkedMap() 68 | maxRunningCount = 1 69 | currentRunningCount = 0 70 | } 71 | 72 | func add(_ operation: BBImageDownloadOperation, preload: Bool) { 73 | if currentRunningCount < maxRunningCount { 74 | currentRunningCount += 1 75 | BBDispatchQueuePool.background.async { [weak self] in 76 | guard self != nil else { return } 77 | operation.start() 78 | } 79 | } else { 80 | let node = BBImageDownloadLinkedMapNode(key: operation.url, value: operation) 81 | if preload { preloadWaitingQueue.enqueue(node) } 82 | else { waitingQueue.enqueue(node) } 83 | } 84 | } 85 | 86 | func removeOperation(forKey key: URL) { 87 | if let node = waitingQueue.dic[key] { 88 | waitingQueue.remove(node) 89 | } else if let node = preloadWaitingQueue.dic[key] { 90 | preloadWaitingQueue.remove(node) 91 | } else if let next = waitingQueue.dequeue()?.value { 92 | BBDispatchQueuePool.background.async { [weak self] in 93 | guard self != nil else { return } 94 | next.start() 95 | } 96 | } else if let next = preloadWaitingQueue.dequeue()?.value { 97 | BBDispatchQueuePool.background.async { [weak self] in 98 | guard self != nil else { return } 99 | next.start() 100 | } 101 | } else { 102 | currentRunningCount -= 1 103 | assert(currentRunningCount >= 0, "currentRunningCount must >= 0") 104 | } 105 | } 106 | 107 | func upgradePreloadOperation(for key: URL) { 108 | if let node = preloadWaitingQueue.dic[key] { 109 | preloadWaitingQueue.remove(node) 110 | node.prev = nil 111 | node.next = nil 112 | waitingQueue.enqueue(node) 113 | } 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /BBWebImage/BBWebImage/Core/BBWeakProxy.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BBWeakProxy.swift 3 | // BBWebImage 4 | // 5 | // Created by Kaibo Lu on 2/6/19. 6 | // Copyright © 2019 Kaibo Lu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public class BBWeakProxy: NSObject { 12 | private weak var target: NSObjectProtocol? 13 | 14 | public init(target: NSObjectProtocol) { 15 | self.target = target 16 | } 17 | 18 | public override func responds(to aSelector: Selector!) -> Bool { 19 | return (target?.responds(to: aSelector) ?? false) || super.responds(to: aSelector) 20 | } 21 | 22 | public override func forwardingTarget(for aSelector: Selector!) -> Any? { 23 | return target 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /BBWebImage/BBWebImage/Core/BBWebCacheResource.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BBWebCacheResource.swift 3 | // BBWebImage 4 | // 5 | // Created by Kaibo Lu on 2018/12/8. 6 | // Copyright © 2018年 Kaibo Lu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | /// BBWebCacheResource defines how to download and cache image 12 | public protocol BBWebCacheResource { 13 | var cacheKey: String { get } 14 | var downloadUrl: URL { get } 15 | } 16 | 17 | extension URL: BBWebCacheResource { 18 | public var cacheKey: String { return absoluteString } 19 | public var downloadUrl: URL { return self } 20 | } 21 | -------------------------------------------------------------------------------- /BBWebImage/BBWebImage/Core/BBWebImageGIFCoder.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BBWebImageGIFCoder.swift 3 | // BBWebImage 4 | // 5 | // Created by Kaibo Lu on 2/6/19. 6 | // Copyright © 2019 Kaibo Lu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import MobileCoreServices 11 | 12 | public class BBWebImageGIFCoder: BBAnimatedImageCoder { 13 | private var imageSource: CGImageSource? 14 | private var imageOrientation: UIImage.Orientation 15 | 16 | public var imageData: Data? { 17 | didSet { 18 | if let data = imageData { 19 | imageSource = CGImageSourceCreateWithData(data as CFData, nil) 20 | if let source = imageSource, 21 | let properties = CGImageSourceCopyProperties(source, nil) as? [CFString : Any], 22 | let rawValue = properties[kCGImagePropertyOrientation] as? UInt32, 23 | let orientation = CGImagePropertyOrientation(rawValue: rawValue) { 24 | imageOrientation = orientation.bb_UIImageOrientation 25 | } 26 | } else { 27 | imageSource = nil 28 | } 29 | } 30 | } 31 | 32 | public var frameCount: Int? { 33 | if let source = imageSource { 34 | let count = CGImageSourceGetCount(source) 35 | if count > 0 { return count } 36 | } 37 | return nil 38 | } 39 | 40 | public var loopCount: Int? { 41 | if let source = imageSource, 42 | let properties = CGImageSourceCopyProperties(source, nil) as? [CFString : Any], 43 | let gifInfo = properties[kCGImagePropertyGIFDictionary] as? [CFString : Any], 44 | let count = gifInfo[kCGImagePropertyGIFLoopCount] as? Int { 45 | return count 46 | } 47 | return nil 48 | } 49 | 50 | public init() { 51 | imageOrientation = .up 52 | } 53 | 54 | public func imageFrame(at index: Int, decompress: Bool) -> UIImage? { 55 | if let source = imageSource, 56 | let sourceImage = CGImageSourceCreateImageAtIndex(source, index, [kCGImageSourceShouldCache : true] as CFDictionary) { 57 | if decompress { 58 | if let cgimage = BBWebImageImageIOCoder.decompressedImage(sourceImage) { 59 | return UIImage(cgImage: cgimage, scale: 1, orientation: imageOrientation) 60 | } 61 | } else { 62 | return UIImage(cgImage: sourceImage, scale: 1, orientation: imageOrientation) 63 | } 64 | } 65 | return nil 66 | } 67 | 68 | public func duration(at index: Int) -> TimeInterval? { 69 | if let source = imageSource, 70 | let properties = CGImageSourceCopyPropertiesAtIndex(source, index, nil) as? [CFString : Any], 71 | let gifInfo = properties[kCGImagePropertyGIFDictionary] as? [CFString : Any] { 72 | var currentDuration: TimeInterval = -1 73 | if let d = gifInfo[kCGImagePropertyGIFUnclampedDelayTime] as? TimeInterval { 74 | currentDuration = d 75 | } else if let d = gifInfo[kCGImagePropertyGIFDelayTime] as? TimeInterval { 76 | currentDuration = d 77 | } 78 | if currentDuration >= 0 { 79 | if currentDuration < 0.01 { currentDuration = 0.1 } 80 | return currentDuration 81 | } 82 | } 83 | return nil 84 | } 85 | 86 | public func imageFrameSize(at index: Int) -> CGSize? { 87 | if let source = imageSource, 88 | let properties = CGImageSourceCopyPropertiesAtIndex(source, index, nil) as? [CFString : Any], 89 | let width = properties[kCGImagePropertyPixelWidth] as? Int, 90 | width > 0, 91 | let height = properties[kCGImagePropertyPixelHeight] as? Int, 92 | height > 0 { 93 | return CGSize(width: width, height: height) 94 | } 95 | return nil 96 | } 97 | } 98 | 99 | extension BBWebImageGIFCoder: BBImageCoder { 100 | public func canDecode(_ data: Data) -> Bool { 101 | return data.bb_imageFormat == .GIF 102 | } 103 | 104 | public func decodedImage(with data: Data) -> UIImage? { 105 | let image = BBAnimatedImage(bb_data: data, decoder: copy() as? BBAnimatedImageCoder) 106 | image?.bb_imageFormat = data.bb_imageFormat 107 | return image 108 | } 109 | 110 | public func decompressedImage(with image: UIImage, data: Data) -> UIImage? { 111 | return nil 112 | } 113 | 114 | public func canEncode(_ format: BBImageFormat) -> Bool { 115 | return format == .GIF 116 | } 117 | 118 | public func encodedData(with image: UIImage, format: BBImageFormat) -> Data? { 119 | if format != .GIF { return nil } 120 | if let animatedImage = image as? BBAnimatedImage, 121 | animatedImage.bb_imageFormat == .GIF { 122 | return animatedImage.bb_originalImageData 123 | } 124 | var sourceImages: [CGImage] = [] 125 | if let images = image.images { 126 | for frame in images { 127 | if let sourceImage = frame.cgImage { 128 | sourceImages.append(sourceImage) 129 | } 130 | } 131 | } 132 | if sourceImages.isEmpty, 133 | let sourceImage = image.cgImage { 134 | sourceImages.append(sourceImage) 135 | } 136 | guard !sourceImages.isEmpty, 137 | let data = CFDataCreateMutable(kCFAllocatorDefault, 0), 138 | let destination = CGImageDestinationCreateWithData(data, kUTTypeGIF, sourceImages.count, nil) else { return nil } 139 | if sourceImages.count == 1 { 140 | CGImageDestinationAddImage(destination, sourceImages.first!, nil) 141 | } else { 142 | let properties: [CFString : Any] = [kCGImagePropertyGIFDictionary : [kCGImagePropertyGIFLoopCount : 0]] 143 | CGImageDestinationSetProperties(destination, properties as CFDictionary) 144 | 145 | let frameDuration = image.duration / Double(image.images!.count) 146 | let frameProperties: [CFString : Any] = [kCGImagePropertyGIFDictionary : [kCGImagePropertyGIFUnclampedDelayTime : frameDuration]] 147 | for sourceImage in sourceImages { 148 | CGImageDestinationAddImage(destination, sourceImage, frameProperties as CFDictionary) 149 | } 150 | } 151 | if CGImageDestinationFinalize(destination) { return data as Data } 152 | return nil 153 | } 154 | 155 | public func copy() -> BBImageCoder { return BBWebImageGIFCoder() } 156 | } 157 | -------------------------------------------------------------------------------- /BBWebImage/BBWebImage/Core/BBWebImageImageIOCoder.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BBWebImageImageIOCoder.swift 3 | // BBWebImage 4 | // 5 | // Created by Kaibo Lu on 2018/10/8. 6 | // Copyright © 2018年 Kaibo Lu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public class BBWebImageImageIOCoder: BBImageCoder { 12 | private var imageSource: CGImageSource? 13 | private var imageWidth: Int 14 | private var imageHeight: Int 15 | private var imageOrientation: UIImage.Orientation 16 | 17 | public init() { 18 | imageWidth = 0 19 | imageHeight = 0 20 | imageOrientation = .up 21 | } 22 | 23 | public func canDecode(_ data: Data) -> Bool { 24 | switch data.bb_imageFormat { 25 | case .JPEG, .PNG: 26 | return true 27 | default: 28 | return false 29 | } 30 | } 31 | 32 | public func decodedImage(with data: Data) -> UIImage? { 33 | let image = UIImage(data: data) 34 | image?.bb_imageFormat = data.bb_imageFormat 35 | return image 36 | } 37 | 38 | public func decompressedImage(with image: UIImage, data: Data) -> UIImage? { 39 | guard let sourceImage = image.cgImage, 40 | let cgimage = BBWebImageImageIOCoder.decompressedImage(sourceImage) else { return image } 41 | let finalImage = UIImage(cgImage: cgimage, scale: image.scale, orientation: image.imageOrientation) 42 | finalImage.bb_imageFormat = image.bb_imageFormat 43 | return finalImage 44 | } 45 | 46 | public static func decompressedImage(_ sourceImage: CGImage) -> CGImage? { 47 | return autoreleasepool { () -> CGImage? in 48 | let width = sourceImage.width 49 | let height = sourceImage.height 50 | var bitmapInfo = sourceImage.bitmapInfo 51 | bitmapInfo.remove(.alphaInfoMask) 52 | if sourceImage.bb_containsAlpha { 53 | bitmapInfo = CGBitmapInfo(rawValue: bitmapInfo.rawValue | CGImageAlphaInfo.premultipliedFirst.rawValue) 54 | } else { 55 | bitmapInfo = CGBitmapInfo(rawValue: bitmapInfo.rawValue | CGImageAlphaInfo.noneSkipFirst.rawValue) 56 | } 57 | guard let context = CGContext(data: nil, 58 | width: width, 59 | height: height, 60 | bitsPerComponent: sourceImage.bitsPerComponent, 61 | bytesPerRow: 0, 62 | space: bb_shareColorSpace, 63 | bitmapInfo: bitmapInfo.rawValue) else { return nil } 64 | context.draw(sourceImage, in: CGRect(x: 0, y: 0, width: width, height: height)) 65 | return context.makeImage() 66 | } 67 | } 68 | 69 | public func canEncode(_ format: BBImageFormat) -> Bool { 70 | return true 71 | } 72 | 73 | public func encodedData(with image: UIImage, format: BBImageFormat) -> Data? { 74 | guard let sourceImage = image.cgImage, 75 | let data = CFDataCreateMutable(kCFAllocatorDefault, 0) else { return nil } 76 | var imageFormat = format 77 | if format == .unknown { imageFormat = sourceImage.bb_containsAlpha ? .PNG : .JPEG } 78 | if let destination = CGImageDestinationCreateWithData(data, imageFormat.UTType, 1, nil) { 79 | let properties = [kCGImagePropertyOrientation : image.imageOrientation.bb_CGImageOrientation.rawValue] 80 | CGImageDestinationAddImage(destination, sourceImage, properties as CFDictionary) 81 | if CGImageDestinationFinalize(destination) { return data as Data } 82 | } 83 | return nil 84 | } 85 | 86 | public func copy() -> BBImageCoder { return BBWebImageImageIOCoder() } 87 | } 88 | 89 | extension BBWebImageImageIOCoder: BBImageProgressiveCoder { 90 | public func canIncrementallyDecode(_ data: Data) -> Bool { 91 | switch data.bb_imageFormat { 92 | case .JPEG, .PNG: 93 | return true 94 | default: 95 | return false 96 | } 97 | } 98 | 99 | public func incrementallyDecodedImage(with data: Data, finished: Bool) -> UIImage? { 100 | if imageSource == nil { 101 | imageSource = CGImageSourceCreateIncremental(nil) 102 | } 103 | guard let source = imageSource else { return nil } 104 | CGImageSourceUpdateData(source, data as CFData, finished) 105 | var image: UIImage? 106 | if imageWidth <= 0 || imageHeight <= 0, 107 | let properties = CGImageSourceCopyPropertiesAtIndex(source, 0, nil) as? [CFString : AnyObject] { 108 | if let width = properties[kCGImagePropertyPixelWidth] as? Int { 109 | imageWidth = width 110 | } 111 | if let height = properties[kCGImagePropertyPixelHeight] as? Int { 112 | imageHeight = height 113 | } 114 | if let rawValue = properties[kCGImagePropertyOrientation] as? UInt32, 115 | let orientation = CGImagePropertyOrientation(rawValue: rawValue) { 116 | imageOrientation = orientation.bb_UIImageOrientation 117 | } 118 | } 119 | if imageWidth > 0 && imageHeight > 0, 120 | let cgimage = CGImageSourceCreateImageAtIndex(source, 0, nil) { 121 | image = UIImage(cgImage: cgimage, scale: 1, orientation: imageOrientation) 122 | image?.bb_imageFormat = data.bb_imageFormat 123 | } 124 | if finished { 125 | imageSource = nil 126 | imageWidth = 0 127 | imageHeight = 0 128 | imageOrientation = .up 129 | } 130 | return image 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /BBWebImage/BBWebImage/Extensions/CALayer+BBWebCache.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CALayer+BBWebCache.swift 3 | // BBWebImage 4 | // 5 | // Created by Kaibo Lu on 2018/12/7. 6 | // Copyright © 2018年 Kaibo Lu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension CALayer: BBWebCache { 12 | /// Sets image with resource, placeholder, custom opotions 13 | /// 14 | /// - Parameters: 15 | /// - resource: image resource specifying how to download and cache image 16 | /// - placeholder: placeholder image displayed when loading image 17 | /// - options: options for some behaviors 18 | /// - editor: editor specifying how to edit and cache image in memory 19 | /// - progress: a closure called while image is downloading 20 | /// - completion: a closure called when image loading is finished 21 | public func bb_setImage(with resource: BBWebCacheResource, 22 | placeholder: UIImage? = nil, 23 | options: BBWebImageOptions = .none, 24 | editor: BBWebImageEditor? = nil, 25 | progress: BBImageDownloaderProgress? = nil, 26 | completion: BBWebImageManagerCompletion? = nil) { 27 | let setImage: BBSetImage = { [weak self] (image) in 28 | if let self = self { self.contents = image?.cgImage } 29 | } 30 | bb_setImage(with: resource, 31 | placeholder: placeholder, 32 | options: options, 33 | editor: editor, 34 | taskKey: bb_imageLoadTaskKey, 35 | setImage: setImage, 36 | progress: progress, 37 | completion: completion) 38 | } 39 | 40 | /// Cancels image loading task 41 | public func bb_cancelImageLoadTask() { 42 | bb_webCacheOperation.task(forKey: bb_imageLoadTaskKey)?.cancel() 43 | } 44 | 45 | public var bb_imageLoadTaskKey: String { return classForCoder.description() } 46 | } 47 | -------------------------------------------------------------------------------- /BBWebImage/BBWebImage/Extensions/Data+ImageFormat.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Data+ImageFormat.swift 3 | // BBWebImage 4 | // 5 | // Created by Kaibo Lu on 2018/10/8. 6 | // Copyright © 2018年 Kaibo Lu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import MobileCoreServices 11 | 12 | public enum BBImageFormat { 13 | case unknown 14 | case JPEG 15 | case PNG 16 | case GIF 17 | 18 | var UTType: CFString { 19 | switch self { 20 | case .JPEG: 21 | return kUTTypeJPEG 22 | case .PNG: 23 | return kUTTypePNG 24 | case .GIF: 25 | return kUTTypeGIF 26 | default: 27 | return kUTTypeImage 28 | } 29 | } 30 | } 31 | 32 | public extension Data { 33 | var bb_imageFormat: BBImageFormat { 34 | if let firstByte = self.first { 35 | switch firstByte { 36 | case 0xFF: return .JPEG // https://en.wikipedia.org/wiki/JPEG 37 | case 0x89: return .PNG // https://en.wikipedia.org/wiki/Portable_Network_Graphics 38 | case 0x47: return .GIF // https://en.wikipedia.org/wiki/GIF 39 | default: return .unknown 40 | } 41 | } 42 | return .unknown 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /BBWebImage/BBWebImage/Extensions/DispatchQueue+Safe.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DispatchQueue+Safe.swift 3 | // BBWebImage 4 | // 5 | // Created by Kaibo Lu on 2018/10/8. 6 | // Copyright © 2018年 Kaibo Lu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension DispatchQueue { 12 | func bb_safeAsync(_ work: @escaping () -> Void) { 13 | if label == String(cString: __dispatch_queue_get_label(nil), encoding: .utf8) { 14 | work() 15 | } else { 16 | async(execute: work) 17 | } 18 | } 19 | 20 | func bb_safeSync(_ work: @escaping () -> Void) { 21 | if label == String(cString: __dispatch_queue_get_label(nil), encoding: .utf8) { 22 | work() 23 | } else { 24 | sync(execute: work) 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /BBWebImage/BBWebImage/Extensions/MKAnnotationView+BBWebCache.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MKAnnotationView+BBWebCache.swift 3 | // BBWebImage 4 | // 5 | // Created by Kaibo Lu on 2018/12/7. 6 | // Copyright © 2018年 Kaibo Lu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import MapKit 11 | 12 | extension MKAnnotationView: BBWebCache { 13 | /// Sets image with resource, placeholder, custom opotions 14 | /// 15 | /// - Parameters: 16 | /// - resource: image resource specifying how to download and cache image 17 | /// - placeholder: placeholder image displayed when loading image 18 | /// - options: options for some behaviors 19 | /// - editor: editor specifying how to edit and cache image in memory 20 | /// - progress: a closure called while image is downloading 21 | /// - completion: a closure called when image loading is finished 22 | public func bb_setImage(with resource: BBWebCacheResource, 23 | placeholder: UIImage? = nil, 24 | options: BBWebImageOptions = .none, 25 | editor: BBWebImageEditor? = nil, 26 | progress: BBImageDownloaderProgress? = nil, 27 | completion: BBWebImageManagerCompletion? = nil) { 28 | let setImage: BBSetImage = { [weak self] (image) in 29 | if let self = self { self.image = image } 30 | } 31 | bb_setImage(with: resource, 32 | placeholder: placeholder, 33 | options: options, 34 | editor: editor, 35 | taskKey: bb_imageLoadTaskKey, 36 | setImage: setImage, 37 | progress: progress, 38 | completion: completion) 39 | } 40 | 41 | /// Cancels image loading task 42 | public func bb_cancelImageLoadTask() { 43 | bb_webCacheOperation.task(forKey: bb_imageLoadTaskKey)?.cancel() 44 | } 45 | 46 | public var bb_imageLoadTaskKey: String { return classForCoder.description() } 47 | } 48 | -------------------------------------------------------------------------------- /BBWebImage/BBWebImage/Extensions/String+MD5.swift: -------------------------------------------------------------------------------- 1 | // 2 | // String+MD5.swift 3 | // BBWebImage 4 | // 5 | // Created by Kaibo Lu on 2018/10/12. 6 | // Copyright © 2018年 Kaibo Lu. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import CommonCrypto 11 | 12 | public extension String { 13 | var bb_md5: String { 14 | guard let data = data(using: .utf8) else { return self } 15 | var digest = [UInt8](repeating: 0, count: Int(CC_MD5_DIGEST_LENGTH)) 16 | _ = data.withUnsafeBytes { (bytes: UnsafeRawBufferPointer) in 17 | return CC_MD5(bytes.baseAddress, CC_LONG(data.count), &digest) 18 | } 19 | return digest.map { String(format: "%02x", $0) }.joined() 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /BBWebImage/BBWebImage/Extensions/UIButton+BBWebCache.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIButton+BBWebCache.swift 3 | // BBWebImage 4 | // 5 | // Created by Kaibo Lu on 2018/10/9. 6 | // Copyright © 2018年 Kaibo Lu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension UIButton: BBWebCache { 12 | /// Sets image with resource, placeholder, custom opotions 13 | /// 14 | /// - Parameters: 15 | /// - resource: image resource specifying how to download and cache image 16 | /// - state: button state to set image 17 | /// - placeholder: placeholder image displayed when loading image 18 | /// - options: options for some behaviors 19 | /// - editor: editor specifying how to edit and cache image in memory 20 | /// - progress: a closure called while image is downloading 21 | /// - completion: a closure called when image loading is finished 22 | public func bb_setImage(with resource: BBWebCacheResource, 23 | forState state: UIControl.State, 24 | placeholder: UIImage? = nil, 25 | options: BBWebImageOptions = .none, 26 | editor: BBWebImageEditor? = nil, 27 | progress: BBImageDownloaderProgress? = nil, 28 | completion: BBWebImageManagerCompletion? = nil) { 29 | let setImage: BBSetImage = { [weak self] (image) in 30 | if let self = self { self.setImage(image, for: state) } 31 | } 32 | bb_setImage(with: resource, 33 | placeholder: placeholder, 34 | options: options, 35 | editor: editor, 36 | taskKey: bb_imageLoadTaskKey(forState: state), 37 | setImage: setImage, 38 | progress: progress, 39 | completion: completion) 40 | } 41 | 42 | /// Cancels image loading task 43 | /// 44 | /// - Parameter state: button state to set image 45 | public func bb_cancelImageLoadTask(forState state: UIControl.State) { 46 | let key = bb_imageLoadTaskKey(forState: state) 47 | bb_webCacheOperation.task(forKey: key)?.cancel() 48 | } 49 | 50 | public func bb_imageLoadTaskKey(forState state: UIControl.State) -> String { 51 | return classForCoder.description() + "Image\(state.rawValue)" 52 | } 53 | 54 | /// Sets background image with resource, placeholder, custom opotions 55 | /// 56 | /// - Parameters: 57 | /// - resource: image resource specifying how to download and cache image 58 | /// - state: button state to set background image 59 | /// - placeholder: placeholder image displayed when loading image 60 | /// - options: options for some behaviors 61 | /// - editor: editor specifying how to edit and cache image in memory 62 | /// - progress: a closure called while image is downloading 63 | /// - completion: a closure called when image loading is finished 64 | public func bb_setBackgroundImage(with resource: BBWebCacheResource, 65 | forState state: UIControl.State, 66 | placeholder: UIImage? = nil, 67 | options: BBWebImageOptions = .none, 68 | editor: BBWebImageEditor? = nil, 69 | progress: BBImageDownloaderProgress? = nil, 70 | completion: BBWebImageManagerCompletion? = nil) { 71 | let setImage: BBSetImage = { [weak self] (image) in 72 | if let self = self { self.setBackgroundImage(image, for: state) } 73 | } 74 | bb_setImage(with: resource, 75 | placeholder: placeholder, 76 | options: options, 77 | editor: editor, 78 | taskKey: bb_backgroundImageLoadTaskKey(forState: state), 79 | setImage: setImage, 80 | progress: progress, 81 | completion: completion) 82 | } 83 | 84 | /// Cancels background image loading task 85 | /// 86 | /// - Parameter state: button state to set background image 87 | public func bb_cancelBackgroundImageLoadTask(forState state: UIControl.State) { 88 | let key = bb_backgroundImageLoadTaskKey(forState: state) 89 | bb_webCacheOperation.task(forKey: key)?.cancel() 90 | } 91 | 92 | public func bb_backgroundImageLoadTaskKey(forState state: UIControl.State) -> String { 93 | return classForCoder.description() + "BackgroundImage\(state.rawValue)" 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /BBWebImage/BBWebImage/Extensions/UIDevice+BBAdd.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIDevice+BBAdd.swift 3 | // BBWebImage 4 | // 5 | // Created by Kaibo Lu on 2/11/19. 6 | // Copyright © 2019 Kaibo Lu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension UIDevice { 12 | static var bb_totalMemory: Int64 { return Int64(ProcessInfo().physicalMemory) } 13 | 14 | static var bb_freeMemory: Int64 { 15 | let host_port = mach_host_self() 16 | var page_size: vm_size_t = 0 17 | guard host_page_size(host_port, &page_size) == KERN_SUCCESS else { return -1 } 18 | var host_size = mach_msg_type_number_t(MemoryLayout.size / MemoryLayout.size) 19 | let hostInfo = vm_statistics_t.allocate(capacity: 1) 20 | let kern = hostInfo.withMemoryRebound(to: integer_t.self, capacity: Int(host_size)) { 21 | host_statistics(host_port, HOST_VM_INFO, $0, &host_size) 22 | } 23 | let vm_stat = hostInfo.move() 24 | hostInfo.deallocate() 25 | guard kern == KERN_SUCCESS else { return -1 } 26 | return Int64(page_size) * Int64(vm_stat.free_count) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /BBWebImage/BBWebImage/Extensions/UIImage+ImageFormat.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIImage+ImageFormat.swift 3 | // BBWebImage 4 | // 5 | // Created by Kaibo Lu on 2018/10/8. 6 | // Copyright © 2018年 Kaibo Lu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | private var imageFormatKey: Void? 12 | private var imageDataKey: Void? 13 | private var imageEditKey: Void? 14 | 15 | public extension UIImage { 16 | var bb_imageFormat: BBImageFormat? { 17 | get { return objc_getAssociatedObject(self, &imageFormatKey) as? BBImageFormat } 18 | set { objc_setAssociatedObject(self, &imageFormatKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } 19 | } 20 | 21 | var bb_imageEditKey: String? { 22 | get { return objc_getAssociatedObject(self, &imageEditKey) as? String } 23 | set { objc_setAssociatedObject(self, &imageEditKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } 24 | } 25 | 26 | var bb_bytes: Int64 { return Int64(size.width * size.height * scale) } 27 | } 28 | 29 | public extension CGImage { 30 | var bb_containsAlpha: Bool { return !(alphaInfo == .none || alphaInfo == .noneSkipFirst || alphaInfo == .noneSkipLast) } 31 | var bb_bytes: Int { return max(1, height * bytesPerRow) } 32 | } 33 | 34 | extension CGImagePropertyOrientation { 35 | var bb_UIImageOrientation: UIImage.Orientation { 36 | switch self { 37 | case .up: return .up 38 | case .down: return .down 39 | case .left: return .left 40 | case .right: return .right 41 | case .upMirrored: return .upMirrored 42 | case .downMirrored: return .downMirrored 43 | case .leftMirrored: return .leftMirrored 44 | case .rightMirrored: return .downMirrored 45 | default: return .up 46 | } 47 | } 48 | } 49 | 50 | extension UIImage.Orientation { 51 | var bb_CGImageOrientation: CGImagePropertyOrientation { 52 | switch self { 53 | case .up: return .up 54 | case .down: return .down 55 | case .left: return .left 56 | case .right: return .right 57 | case .upMirrored: return .upMirrored 58 | case .downMirrored: return .downMirrored 59 | case .leftMirrored: return .leftMirrored 60 | case .rightMirrored: return .rightMirrored 61 | default: return .up 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /BBWebImage/BBWebImage/Extensions/UIImageView+BBWebCache.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIImageView+BBWebCache.swift 3 | // BBWebImage 4 | // 5 | // Created by Kaibo Lu on 2018/10/9. 6 | // Copyright © 2018年 Kaibo Lu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension UIImageView: BBWebCache { 12 | /// Sets image with resource, placeholder, custom opotions 13 | /// 14 | /// - Parameters: 15 | /// - resource: image resource specifying how to download and cache image 16 | /// - placeholder: placeholder image displayed when loading image 17 | /// - options: options for some behaviors 18 | /// - editor: editor specifying how to edit and cache image in memory 19 | /// - progress: a closure called while image is downloading 20 | /// - completion: a closure called when image loading is finished 21 | public func bb_setImage(with resource: BBWebCacheResource, 22 | placeholder: UIImage? = nil, 23 | options: BBWebImageOptions = .none, 24 | editor: BBWebImageEditor? = nil, 25 | progress: BBImageDownloaderProgress? = nil, 26 | completion: BBWebImageManagerCompletion? = nil) { 27 | let setImage: BBSetImage = { [weak self] (image) in 28 | if let self = self { self.image = image } 29 | } 30 | bb_setImage(with: resource, 31 | placeholder: placeholder, 32 | options: options, 33 | editor: editor, 34 | taskKey: bb_imageLoadTaskKey, 35 | setImage: setImage, 36 | progress: progress, 37 | completion: completion) 38 | } 39 | 40 | /// Cancels image loading task 41 | public func bb_cancelImageLoadTask() { 42 | bb_webCacheOperation.task(forKey: bb_imageLoadTaskKey)?.cancel() 43 | } 44 | 45 | public var bb_imageLoadTaskKey: String { return classForCoder.description() } 46 | 47 | /// Sets highlighted image with resource, placeholder, custom opotions 48 | /// 49 | /// - Parameters: 50 | /// - resource: image resource specifying how to download and cache image 51 | /// - placeholder: placeholder image displayed when loading image 52 | /// - options: options for some behaviors 53 | /// - editor: editor specifying how to edit and cache image in memory 54 | /// - progress: a closure called while image is downloading 55 | /// - completion: a closure called when image loading is finished 56 | public func bb_setHighlightedImage(with resource: BBWebCacheResource, 57 | placeholder: UIImage? = nil, 58 | options: BBWebImageOptions = .none, 59 | editor: BBWebImageEditor? = nil, 60 | progress: BBImageDownloaderProgress? = nil, 61 | completion: BBWebImageManagerCompletion? = nil) { 62 | let setImage: BBSetImage = { [weak self] (image) in 63 | if let self = self { self.highlightedImage = image } 64 | } 65 | bb_setImage(with: resource, 66 | placeholder: placeholder, 67 | options: options, 68 | editor: editor, 69 | taskKey: bb_highlightedImageLoadTaskKey, 70 | setImage: setImage, 71 | progress: progress, 72 | completion: completion) 73 | } 74 | 75 | /// Cancels highlighted image loading task 76 | public func bb_cancelHighlightedImageLoadTask() { 77 | bb_webCacheOperation.task(forKey: bb_highlightedImageLoadTaskKey)?.cancel() 78 | } 79 | 80 | public var bb_highlightedImageLoadTaskKey: String { return classForCoder.description() + "Highlighted" } 81 | } 82 | -------------------------------------------------------------------------------- /BBWebImage/BBWebImage/Filter/BBCILookup.cikernel: -------------------------------------------------------------------------------- 1 | kernel vec4 filterKernel(sampler inputImage, sampler inputLUT, float intensity) { 2 | vec4 textureColor = sample(inputImage,samplerCoord(inputImage)); 3 | textureColor = clamp(textureColor, vec4(0.0), vec4(1.0)); 4 | 5 | float blueColor = textureColor.b * 63.0; 6 | 7 | vec2 quad1; 8 | quad1.y = floor(floor(blueColor) / 8.0); 9 | quad1.x = floor(blueColor) - (quad1.y * 8.0); 10 | 11 | vec2 quad2; 12 | quad2.y = floor(ceil(blueColor) / 8.0); 13 | quad2.x = ceil(blueColor) - (quad2.y * 8.0); 14 | 15 | vec2 texPos1; 16 | texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r); 17 | texPos1.y = (quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g); 18 | 19 | vec2 texPos2; 20 | texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r); 21 | texPos2.y = (quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g); 22 | 23 | texPos1.y = 1.0 - texPos1.y; 24 | texPos2.y = 1.0 - texPos2.y; 25 | 26 | vec4 inputLUTExtent = samplerExtent(inputLUT); 27 | 28 | vec4 newColor1 = sample(inputLUT, samplerTransform(inputLUT, texPos1 * vec2(512.0) + inputLUTExtent.xy)); 29 | vec4 newColor2 = sample(inputLUT, samplerTransform(inputLUT, texPos2 * vec2(512.0) + inputLUTExtent.xy)); 30 | 31 | vec4 newColor = mix(newColor1, newColor2, fract(blueColor)); 32 | return mix(textureColor, vec4(newColor.rgb, textureColor.a), intensity); 33 | } 34 | -------------------------------------------------------------------------------- /BBWebImage/BBWebImage/Filter/BBCILookupFilter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BBCILookupFilter.swift 3 | // BBWebImage 4 | // 5 | // Created by Kaibo Lu on 2018/11/27. 6 | // Copyright © 2018年 Kaibo Lu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | open class BBCILookupFilter: CIFilter { 12 | private static var _kernel: CIKernel? 13 | 14 | private static var kernel: CIKernel? { 15 | var localKernel = _kernel // Use local var to prevent multithreading problem 16 | if localKernel == nil { 17 | localKernel = CIKernel(source: kernelString) 18 | _kernel = localKernel 19 | } 20 | return localKernel 21 | } 22 | 23 | private static var kernelString: String { 24 | let path = Bundle(for: self).path(forResource: "BBCILookup", ofType: "cikernel")! 25 | return try! String(contentsOfFile: path, encoding: String.Encoding.utf8) 26 | } 27 | 28 | open class func clear() { _kernel = nil } 29 | 30 | public static func outputImage(withInputImage inputImage: CIImage, lookupTable: CIImage, intensity: CGFloat) -> CIImage? { 31 | return kernel?.apply(extent: inputImage.extent, roiCallback: { (index: Int32, destRect: CGRect) -> CGRect in 32 | if index == 0 { return destRect } 33 | return lookupTable.extent 34 | }, arguments: [inputImage, lookupTable, intensity]) 35 | } 36 | 37 | @objc public var inputImage: CIImage? // Add `@objc` to make it key-value coding compliant. Or it will crash in iOS 8 38 | 39 | public var lookupTable: CIImage? 40 | 41 | private var _intensity: CGFloat 42 | public var intensity: CGFloat { 43 | get { return _intensity } 44 | set { _intensity = min(1, max(0, newValue)) } 45 | } 46 | 47 | open override var outputImage: CIImage? { 48 | if let inputImage = inputImage, 49 | let lookupTable = lookupTable { 50 | return BBCILookupFilter.outputImage(withInputImage: inputImage, lookupTable: lookupTable, intensity: _intensity) 51 | } 52 | return nil 53 | } 54 | 55 | public override init() { 56 | _intensity = 1 57 | super.init() 58 | } 59 | 60 | required public init?(coder aDecoder: NSCoder) { 61 | fatalError("init(coder:) has not been implemented") 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /BBWebImage/BBWebImage/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | 22 | 23 | -------------------------------------------------------------------------------- /BBWebImage/BBWebImageTests/BBImageCoderManagerTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BBImageCoderManagerTests.swift 3 | // BBWebImageTests 4 | // 5 | // Created by Kaibo Lu on 2018/12/4. 6 | // Copyright © 2018年 Kaibo Lu. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | class BBImageCoderManagerTests: XCTestCase { 12 | var coder: BBImageCoderManager! 13 | 14 | var pngData: Data { 15 | let url = Bundle(for: classForCoder).url(forResource: "mew_baseline", withExtension: "png")! 16 | return try! Data(contentsOf: url) 17 | } 18 | 19 | var jpgData: Data { 20 | let url = Bundle(for: classForCoder).url(forResource: "mew_baseline", withExtension: "jpg")! 21 | return try! Data(contentsOf: url) 22 | } 23 | 24 | var gifData: Data { 25 | let url = Bundle(for: classForCoder).url(forResource: "Rotating_earth", withExtension: "gif")! 26 | return try! Data(contentsOf: url) 27 | } 28 | 29 | override func setUp() { 30 | coder = BBImageCoderManager() 31 | } 32 | 33 | override func tearDown() {} 34 | 35 | func testCanDecode() { 36 | XCTAssertFalse(coder.canDecode(Data())) 37 | XCTAssertTrue(coder.canDecode(pngData)) 38 | XCTAssertTrue(coder.canDecode(jpgData)) 39 | XCTAssertTrue(coder.canDecode(gifData)) 40 | } 41 | 42 | func testDecode() { 43 | XCTAssertNil(coder.decodedImage(with: Data())) 44 | 45 | let pngImage = coder.decodedImage(with: pngData) 46 | XCTAssertNotNil(pngImage) 47 | XCTAssertEqual(pngImage?.bb_imageFormat, .PNG) 48 | 49 | let jpgImage = coder.decodedImage(with: jpgData) 50 | XCTAssertNotNil(jpgImage) 51 | XCTAssertEqual(jpgImage?.bb_imageFormat, .JPEG) 52 | 53 | let gifImage = coder.decodedImage(with: gifData) 54 | XCTAssertNotNil(gifImage) 55 | XCTAssertEqual(gifImage?.bb_imageFormat, .GIF) 56 | XCTAssertTrue(gifImage is BBAnimatedImage) 57 | } 58 | 59 | func testDecompress() { 60 | let pngData = self.pngData 61 | let pngImage = coder.decodedImage(with: pngData)! 62 | let pngDecompressedImage = coder.decompressedImage(with: pngImage, data: pngData) 63 | XCTAssertNotNil(pngDecompressedImage) 64 | XCTAssertNotEqual(pngImage, pngDecompressedImage) 65 | XCTAssertEqual(pngDecompressedImage?.bb_imageFormat, .PNG) 66 | 67 | let jpgData = self.jpgData 68 | let jpgImage = coder.decodedImage(with: jpgData)! 69 | let jpgDecompressedImage = coder.decompressedImage(with: jpgImage, data: jpgData) 70 | XCTAssertNotNil(jpgDecompressedImage) 71 | XCTAssertNotEqual(jpgImage, jpgDecompressedImage) 72 | XCTAssertEqual(jpgDecompressedImage?.bb_imageFormat, .JPEG) 73 | 74 | let gifData = self.gifData 75 | let gifImage = coder.decodedImage(with: gifData)! 76 | let gifDecompressedImage = coder.decompressedImage(with: gifImage, data: gifData) 77 | XCTAssertNil(gifDecompressedImage) 78 | } 79 | 80 | func testCanEncode() { 81 | XCTAssertTrue(coder.canEncode(.unknown)) 82 | XCTAssertTrue(coder.canEncode(.PNG)) 83 | XCTAssertTrue(coder.canEncode(.JPEG)) 84 | XCTAssertTrue(coder.canEncode(.GIF)) 85 | } 86 | 87 | func testEncode() { 88 | let images: [UIImage] = [coder.decodedImage(with: pngData)!, 89 | coder.decodedImage(with: jpgData)!, 90 | coder.decodedImage(with: gifData)!] 91 | for image in images { 92 | XCTAssertNotNil(coder.encodedData(with: image, format: .PNG)) 93 | XCTAssertNotNil(coder.encodedData(with: image, format: .JPEG)) 94 | XCTAssertNotNil(coder.encodedData(with: image, format: .GIF)) 95 | XCTAssertNotNil(coder.encodedData(with: image, format: .unknown)) 96 | } 97 | } 98 | 99 | func testCopy() { 100 | XCTAssertFalse(coder === coder.copy()) 101 | } 102 | 103 | func testCanIncrementallyDecode() { 104 | XCTAssertTrue(coder.canIncrementallyDecode(pngData)) 105 | XCTAssertTrue(coder.canIncrementallyDecode(jpgData)) 106 | XCTAssertFalse(coder.canIncrementallyDecode(gifData)) 107 | } 108 | 109 | func testIncrementallyDecodedImage() { 110 | let test = { (data: Data, total: Int, canDecode: Bool) -> Void in 111 | for i in 1...total { 112 | let finished = (i == total) 113 | let end = Int((Double(i) / Double(total)) * Double(data.count)) 114 | let subdata = data.subdata(in: 0.. Void in 32 | XCTAssertNotNil(gifImage) 33 | XCTAssertEqual(gifImage?.bb_imageFormat, .GIF) 34 | XCTAssertTrue(gifImage is BBAnimatedImage) 35 | } 36 | test(coder.decodedImage(with: gifData)) 37 | test(BBAnimatedImage(bb_data: gifData)) 38 | test(BBAnimatedImage(bb_data: gifData, decoder: coder)) 39 | } 40 | 41 | func testDecompress() { 42 | let gifData = self.gifData 43 | let gifImage = coder.decodedImage(with: gifData)! 44 | let gifDecompressedImage = coder.decompressedImage(with: gifImage, data: gifData) 45 | XCTAssertNil(gifDecompressedImage) 46 | } 47 | 48 | func testCanEncode() { 49 | XCTAssertFalse(coder.canEncode(.unknown)) 50 | XCTAssertFalse(coder.canEncode(.PNG)) 51 | XCTAssertFalse(coder.canEncode(.JPEG)) 52 | XCTAssertTrue(coder.canEncode(.GIF)) 53 | } 54 | 55 | func testEncode() { 56 | let test = { (image: UIImage) -> Void in 57 | XCTAssertNil(self.coder.encodedData(with: image, format: .unknown)) 58 | XCTAssertNil(self.coder.encodedData(with: image, format: .PNG)) 59 | XCTAssertNil(self.coder.encodedData(with: image, format: .JPEG)) 60 | XCTAssertNotNil(self.coder.encodedData(with: image, format: .GIF)) 61 | } 62 | 63 | let image = coder.decodedImage(with: gifData) as! BBAnimatedImage 64 | test(image) 65 | 66 | let jpgUrl = Bundle(for: classForCoder).url(forResource: "mew_baseline", withExtension: "jpg")! 67 | let jpgData = try! Data(contentsOf: jpgUrl) 68 | let jpgImage = UIImage(data: jpgData)! 69 | test(jpgImage) 70 | 71 | let pngUrl = Bundle(for: classForCoder).url(forResource: "mew_baseline", withExtension: "png")! 72 | let pngData = try! Data(contentsOf: pngUrl) 73 | let pngImage = UIImage(data: pngData)! 74 | test(pngImage) 75 | 76 | let images = UIImage.animatedImage(with: [jpgImage, pngImage], duration: 2)! 77 | test(images) 78 | } 79 | 80 | func testCopy() { 81 | XCTAssertFalse(coder === coder.copy()) 82 | } 83 | 84 | func testImageData() { 85 | XCTAssertNil(coder.imageData) 86 | coder.imageData = gifData 87 | XCTAssertEqual(coder.imageData, gifData) 88 | } 89 | 90 | func testFrameCount() { 91 | coder.imageData = gifData 92 | XCTAssertEqual(coder.frameCount, 44) 93 | } 94 | 95 | func testLoopCount() { 96 | coder.imageData = gifData 97 | XCTAssertEqual(coder.loopCount, 65535) 98 | } 99 | 100 | func testImageFrame() { 101 | coder.imageData = gifData 102 | for i in 0.. 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /BBWebImage/BBWebImageTests/TestImageDownloadTask.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TestImageDownloadTask.swift 3 | // BBWebImageTests 4 | // 5 | // Created by Kaibo Lu on 1/28/19. 6 | // Copyright © 2019 Kaibo Lu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class TestImageDownloadTask: BBImageDownloadTask { 12 | private(set) var sentinel: Int32 13 | private(set) var url: URL 14 | private(set) var isCancelled: Bool 15 | private(set) var progress: BBImageDownloaderProgress? 16 | private(set) var completion: BBImageDownloaderCompletion 17 | 18 | init(sentinel: Int32, url: URL, progress: BBImageDownloaderProgress?, completion: @escaping BBImageDownloaderCompletion) { 19 | self.sentinel = sentinel 20 | self.url = url 21 | self.isCancelled = false 22 | self.progress = progress 23 | self.completion = { (_, _) in 24 | completion(nil, NSError(domain: "TestError", code: 0, userInfo: nil)) 25 | } 26 | } 27 | 28 | func cancel() { isCancelled = true } 29 | } 30 | -------------------------------------------------------------------------------- /BBWebImage/BBWebImageTests/mew_baseline.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/BBWebImage/BBWebImageTests/mew_baseline.jpg -------------------------------------------------------------------------------- /BBWebImage/BBWebImageTests/mew_baseline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/BBWebImage/BBWebImageTests/mew_baseline.png -------------------------------------------------------------------------------- /BBWebImageDemo/BBWebImageDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /BBWebImageDemo/BBWebImageDemo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /BBWebImageDemo/BBWebImageDemo/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // BBWebImageDemo 4 | // 5 | // Created by Kaibo Lu on 2018/10/3. 6 | // Copyright © 2018年 Kaibo Lu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(_ application: UIApplication) { 23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 24 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(_ application: UIApplication) { 28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(_ application: UIApplication) { 33 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(_ application: UIApplication) { 37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 38 | } 39 | 40 | func applicationWillTerminate(_ application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /BBWebImageDemo/BBWebImageDemo/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /BBWebImageDemo/BBWebImageDemo/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /BBWebImageDemo/BBWebImageDemo/BBCILookupTestFilter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BBCILookupTestFilter.swift 3 | // BBWebImageDemo 4 | // 5 | // Created by Kaibo Lu on 2018/11/29. 6 | // Copyright © 2018年 Kaibo Lu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import BBWebImage 11 | 12 | class BBCILookupTestFilter: BBCILookupFilter { 13 | private static var _sharedLookupTable: CIImage? 14 | private static var sharedLookupTable: CIImage? { 15 | var localLookupTable = _sharedLookupTable 16 | if localLookupTable == nil { 17 | let url = Bundle.main.url(forResource: "test_lookup", withExtension: "png")! 18 | localLookupTable = CIImage(contentsOf: url) 19 | _sharedLookupTable = localLookupTable 20 | } 21 | return localLookupTable 22 | } 23 | 24 | override static func clear() { 25 | _sharedLookupTable = nil 26 | super.clear() 27 | } 28 | 29 | override init() { 30 | super.init() 31 | lookupTable = BBCILookupTestFilter.sharedLookupTable 32 | } 33 | 34 | required init?(coder aDecoder: NSCoder) { 35 | fatalError("init(coder:) has not been implemented") 36 | } 37 | } 38 | 39 | 40 | // The higher maxTileSize, the less memory cost, the longer processing time 41 | public func bb_imageEditorCILookupTestFilter(maxTileSize: Int = 0) -> BBWebImageEditor { 42 | let edit: BBWebImageEditMethod = { (image) in 43 | autoreleasepool { () -> UIImage? in 44 | var inputImage: CIImage? 45 | if let ciimage = image.ciImage { 46 | inputImage = ciimage 47 | } else if let cgimage = image.cgImage { 48 | inputImage = CIImage(cgImage: cgimage) 49 | } else { 50 | inputImage = CIImage(image: image) 51 | } 52 | guard let input = inputImage else { return image } 53 | let filter = BBCILookupTestFilter() 54 | if maxTileSize <= 0 { 55 | filter.inputImage = input 56 | if let output = filter.outputImage, 57 | let sourceImage = bb_shareCIContext.createCGImage(output, from: output.extent), 58 | let cgimage = BBWebImageImageIOCoder.decompressedImage(sourceImage) { 59 | // It costs more memory without decompressing 60 | return UIImage(cgImage: cgimage) 61 | } 62 | return image 63 | } 64 | // Split image into tiles, process tiles and combine 65 | let width = input.extent.width 66 | var height = max(1, floor(CGFloat(maxTileSize) / width)) 67 | var y: CGFloat = 0 68 | var context: CGContext? 69 | while y < input.extent.height { 70 | if y + height > input.extent.height { 71 | height = input.extent.height - y 72 | } 73 | let success = autoreleasepool { () -> Bool in 74 | filter.inputImage = input.cropped(to: CGRect(x: 0, y: y, width: width, height: height)) 75 | guard let output = filter.outputImage, 76 | let cgimage = bb_shareCIContext.createCGImage(output, from: output.extent) else { return false } 77 | if context == nil { 78 | var bitmapInfo = cgimage.bitmapInfo 79 | bitmapInfo.remove(.alphaInfoMask) 80 | if cgimage.bb_containsAlpha { 81 | bitmapInfo = CGBitmapInfo(rawValue: bitmapInfo.rawValue | CGImageAlphaInfo.premultipliedFirst.rawValue) 82 | } else { 83 | bitmapInfo = CGBitmapInfo(rawValue: bitmapInfo.rawValue | CGImageAlphaInfo.noneSkipFirst.rawValue) 84 | } 85 | context = CGContext(data: nil, 86 | width: Int(width), 87 | height: Int(input.extent.height), 88 | bitsPerComponent: cgimage.bitsPerComponent, 89 | bytesPerRow: 0, 90 | space: bb_shareColorSpace, 91 | bitmapInfo: bitmapInfo.rawValue) 92 | if (context == nil) { return false } 93 | } 94 | context?.draw(cgimage, in: CGRect(x: 0, y: y, width: width, height: height)) 95 | return true 96 | } 97 | if !success { return image } 98 | y += height 99 | } 100 | return context?.makeImage().flatMap { UIImage(cgImage: $0) } ?? image 101 | } 102 | } 103 | return BBWebImageEditor(key: BBCILookupTestFilter.description(), edit: edit) 104 | } 105 | -------------------------------------------------------------------------------- /BBWebImageDemo/BBWebImageDemo/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /BBWebImageDemo/BBWebImageDemo/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /BBWebImageDemo/BBWebImageDemo/GIFWallCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GIFWallCell.swift 3 | // BBWebImageDemo 4 | // 5 | // Created by Kaibo Lu on 2/12/19. 6 | // Copyright © 2019 Kaibo Lu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import BBWebImage 11 | 12 | class GIFWallCell: UICollectionViewCell { 13 | private var imageView: UIImageView! 14 | 15 | override init(frame: CGRect) { 16 | super.init(frame: frame) 17 | 18 | imageView = BBAnimatedImageView(frame: CGRect(origin: .zero, size: frame.size)) 19 | imageView.contentMode = .scaleAspectFill 20 | imageView.clipsToBounds = true 21 | contentView.addSubview(imageView) 22 | } 23 | 24 | required init?(coder aDecoder: NSCoder) { 25 | fatalError("init(coder:) has not been implemented") 26 | } 27 | 28 | func set(url: URL) { 29 | let editor = bb_imageEditorCommon(with: imageView.frame.size, 30 | corner: .allCorners, 31 | cornerRadius: 5, 32 | borderWidth: 1, 33 | borderColor: .yellow, 34 | backgroundColor: .gray) 35 | imageView.bb_setImage(with: url, 36 | placeholder: UIImage(named: "placeholder"), 37 | options: .none, 38 | editor: editor, 39 | progress: nil, 40 | completion: nil) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /BBWebImageDemo/BBWebImageDemo/GIFWallVC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GIFWallVC.swift 3 | // BBWebImageDemo 4 | // 5 | // Created by Kaibo Lu on 2/12/19. 6 | // Copyright © 2019 Kaibo Lu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class GIFWallVC: UIViewController { 12 | 13 | override func viewDidLoad() { 14 | super.viewDidLoad() 15 | 16 | let layout = UICollectionViewFlowLayout() 17 | layout.minimumLineSpacing = 0 18 | layout.minimumInteritemSpacing = 0 19 | let cellWidth = view.bounds.width / 4 20 | layout.itemSize = CGSize(width: cellWidth, height: cellWidth) 21 | let colletionView = UICollectionView(frame: view.bounds, collectionViewLayout: layout) 22 | colletionView.register(GIFWallCell.self, forCellWithReuseIdentifier: GIFWallCell.description()) 23 | colletionView.backgroundColor = .gray 24 | colletionView.dataSource = self 25 | view.addSubview(colletionView) 26 | } 27 | } 28 | 29 | extension GIFWallVC: UICollectionViewDataSource { 30 | func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { 31 | return ImageURLProvider.gifUrlStrings.count 32 | } 33 | 34 | func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { 35 | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: GIFWallCell.description(), for: indexPath) as! GIFWallCell 36 | if let url = ImageURLProvider.gifURL(forIndex: indexPath.item) { 37 | cell.set(url: url) 38 | } 39 | return cell 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /BBWebImageDemo/BBWebImageDemo/ImageWallCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImageWallCell.swift 3 | // BBWebImageDemo 4 | // 5 | // Created by Kaibo Lu on 2018/11/7. 6 | // Copyright © 2018年 Kaibo Lu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import BBWebImage 11 | 12 | class ImageWallCell: UICollectionViewCell { 13 | private var imageView: UIImageView! 14 | 15 | override init(frame: CGRect) { 16 | super.init(frame: frame) 17 | 18 | imageView = UIImageView(frame: CGRect(origin: .zero, size: frame.size)) 19 | imageView.contentMode = .scaleAspectFill 20 | imageView.clipsToBounds = true 21 | contentView.addSubview(imageView) 22 | } 23 | 24 | required init?(coder aDecoder: NSCoder) { 25 | fatalError("init(coder:) has not been implemented") 26 | } 27 | 28 | func set(url: URL) { 29 | let editor = bb_imageEditorCommon(with: imageView.frame.size, 30 | corner: .allCorners, 31 | cornerRadius: 5, 32 | borderWidth: 1, 33 | borderColor: .yellow, 34 | backgroundColor: .gray) 35 | imageView.bb_setImage(with: url, 36 | placeholder: UIImage(named: "placeholder"), 37 | options: .progressiveDownload, 38 | editor: editor, 39 | progress: nil, 40 | completion: nil) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /BBWebImageDemo/BBWebImageDemo/ImageWallVC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImageWallVC.swift 3 | // BBWebImageDemo 4 | // 5 | // Created by Kaibo Lu on 2018/11/7. 6 | // Copyright © 2018年 Kaibo Lu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import BBWebImage 11 | 12 | class ImageWallVC: UIViewController { 13 | 14 | var preloadTasks: [Int : BBWebImageLoadTask]! 15 | 16 | override func viewDidLoad() { 17 | super.viewDidLoad() 18 | 19 | preloadTasks = [:] 20 | 21 | let layout = UICollectionViewFlowLayout() 22 | layout.minimumLineSpacing = 0 23 | layout.minimumInteritemSpacing = 0 24 | let cellWidth = view.bounds.width / 4 25 | layout.itemSize = CGSize(width: cellWidth, height: cellWidth) 26 | let colletionView = UICollectionView(frame: view.bounds, collectionViewLayout: layout) 27 | colletionView.register(ImageWallCell.self, forCellWithReuseIdentifier: ImageWallCell.description()) 28 | colletionView.backgroundColor = .gray 29 | colletionView.dataSource = self 30 | if #available(iOS 10.0, *) { colletionView.prefetchDataSource = self } 31 | view.addSubview(colletionView) 32 | } 33 | } 34 | 35 | extension ImageWallVC: UICollectionViewDataSource { 36 | func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { 37 | return 4000 38 | } 39 | 40 | func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { 41 | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: ImageWallCell.description(), for: indexPath) as! ImageWallCell 42 | if let url = ImageURLProvider.originURL(forIndex: indexPath.item + 1) { 43 | cell.set(url: url) 44 | } 45 | return cell 46 | } 47 | } 48 | 49 | extension ImageWallVC: UICollectionViewDataSourcePrefetching { 50 | func collectionView(_ collectionView: UICollectionView, prefetchItemsAt indexPaths: [IndexPath]) { 51 | var urls: [URL] = [] 52 | for indexPath in indexPaths { 53 | if let url = ImageURLProvider.originURL(forIndex: indexPath.item + 1) { 54 | urls.append(url) 55 | } 56 | } 57 | let tasks = BBWebImageManager.shared.preload(urls, options: .none, progress: { (successCount, finishCount, total) in 58 | // print("Preload progress. success count = \(successCount), finish count = \(finishCount), total = \(total)") 59 | }) { (successCount, total) in 60 | // print("Preload completion. success count = \(successCount), total = \(total)") 61 | } 62 | for i in 0.. 2 | 3 | 4 | 5 | NSAppTransportSecurity 6 | 7 | NSAllowsArbitraryLoads 8 | 9 | 10 | CFBundleDevelopmentRegion 11 | $(DEVELOPMENT_LANGUAGE) 12 | CFBundleExecutable 13 | $(EXECUTABLE_NAME) 14 | CFBundleIdentifier 15 | $(PRODUCT_BUNDLE_IDENTIFIER) 16 | CFBundleInfoDictionaryVersion 17 | 6.0 18 | CFBundleName 19 | $(PRODUCT_NAME) 20 | CFBundlePackageType 21 | APPL 22 | CFBundleShortVersionString 23 | 1.0 24 | CFBundleVersion 25 | 1 26 | LSRequiresIPhoneOS 27 | 28 | UILaunchStoryboardName 29 | LaunchScreen 30 | UIMainStoryboardFile 31 | Main 32 | UIRequiredDeviceCapabilities 33 | 34 | armv7 35 | 36 | UISupportedInterfaceOrientations 37 | 38 | UIInterfaceOrientationPortrait 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UISupportedInterfaceOrientations~ipad 43 | 44 | UIInterfaceOrientationPortrait 45 | UIInterfaceOrientationPortraitUpsideDown 46 | UIInterfaceOrientationLandscapeLeft 47 | UIInterfaceOrientationLandscapeRight 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /BBWebImageDemo/BBWebImageDemo/MainMenuVC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MainMenuVC.swift 3 | // BBWebImageDemo 4 | // 5 | // Created by Kaibo Lu on 2018/10/3. 6 | // Copyright © 2018年 Kaibo Lu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import BBWebImage 11 | 12 | class MainMenuVC: UIViewController { 13 | 14 | @IBOutlet private weak var tableView: UITableView! 15 | 16 | private var list: [(String, () -> Void)]! 17 | 18 | override func viewDidLoad() { 19 | super.viewDidLoad() 20 | 21 | automaticallyAdjustsScrollViewInsets = false 22 | 23 | let test = { [weak self] in 24 | if let self = self { self.navigationController?.pushViewController(TestVC(), animated: true) } 25 | } 26 | let filter = { [weak self] in 27 | if let self = self { self.navigationController?.pushViewController(TestFilterVC(), animated: true) } 28 | } 29 | let gif = { [weak self] in 30 | if let self = self { self.navigationController?.pushViewController(TestGIFVC(), animated: true) } 31 | } 32 | let imageWall = { [weak self] in 33 | if let self = self { self.navigationController?.pushViewController(ImageWallVC(), animated: true) } 34 | } 35 | let gifWall = { [weak self] in 36 | if let self = self { self.navigationController?.pushViewController(GIFWallVC(), animated: true) } 37 | } 38 | let clearCache = { [weak self] in 39 | if self != nil { BBWebImageManager.shared.imageCache.clear(.all, completion: nil) } 40 | } 41 | list = [("Test", test), 42 | ("Test filter", filter), 43 | ("Test GIF", gif), 44 | ("Image wall", imageWall), 45 | ("GIF wall", gifWall), 46 | ("Clear cache", clearCache)] 47 | 48 | tableView.register(UITableViewCell.self, forCellReuseIdentifier: UITableViewCell.description()) 49 | tableView.dataSource = self 50 | tableView.delegate = self 51 | } 52 | } 53 | 54 | extension MainMenuVC: UITableViewDataSource { 55 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 56 | return list.count 57 | } 58 | 59 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 60 | let cell = tableView.dequeueReusableCell(withIdentifier: UITableViewCell.description(), for: indexPath) 61 | cell.textLabel?.text = list[indexPath.row].0 62 | return cell 63 | } 64 | } 65 | 66 | extension MainMenuVC: UITableViewDelegate { 67 | func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 68 | tableView.deselectRow(at: indexPath, animated: true) 69 | list[indexPath.row].1() 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /BBWebImageDemo/BBWebImageDemo/Rotating_earth.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/BBWebImageDemo/BBWebImageDemo/Rotating_earth.gif -------------------------------------------------------------------------------- /BBWebImageDemo/BBWebImageDemo/TestFilterVC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TestFilterVC.swift 3 | // BBWebImageDemo 4 | // 5 | // Created by Kaibo Lu on 2018/11/28. 6 | // Copyright © 2018年 Kaibo Lu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import BBWebImage 11 | 12 | class TestFilterVC: UIViewController { 13 | 14 | private var imageView: UIImageView! 15 | private var button: UIButton! 16 | private var filtered: Bool = false 17 | 18 | override func viewDidLoad() { 19 | super.viewDidLoad() 20 | 21 | view.backgroundColor = .white 22 | 23 | imageView = UIImageView(frame: CGRect(x: 10, y: 100, width: view.bounds.width - 20, height: view.bounds.height - 200)) 24 | imageView.contentMode = .scaleAspectFill 25 | imageView.clipsToBounds = true 26 | imageView.image = UIImage(named: "sunflower.jpg") 27 | view.addSubview(imageView) 28 | 29 | button = UIButton(frame: CGRect(x: 10, y: imageView.frame.maxY + 10, width: imageView.frame.width, height: 30)) 30 | button.backgroundColor = .blue 31 | button.setTitle("Add filter", for: .normal) 32 | button.setTitle("Reset", for: .selected) 33 | button.addTarget(self, action: #selector(clickButton), for: .touchUpInside) 34 | view.addSubview(button) 35 | } 36 | 37 | @objc private func clickButton() { 38 | filtered = !filtered 39 | if filtered { 40 | let editor = bb_imageEditorCILookupTestFilter() 41 | imageView.image = editor.edit(imageView.image!) 42 | } else { 43 | imageView.image = UIImage(named: "sunflower.jpg") 44 | } 45 | button.isSelected = filtered 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /BBWebImageDemo/BBWebImageDemo/TestGIFVC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TestGIFVC.swift 3 | // BBWebImageDemo 4 | // 5 | // Created by Kaibo Lu on 2/6/19. 6 | // Copyright © 2019 Kaibo Lu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import BBWebImage 11 | 12 | class TestGIFVC: UIViewController { 13 | 14 | private var imageView: BBAnimatedImageView! 15 | private var scaleLabel: UILabel! 16 | 17 | private var editor: BBWebImageEditor? 18 | 19 | override func viewDidLoad() { 20 | super.viewDidLoad() 21 | 22 | automaticallyAdjustsScrollViewInsets = false 23 | view.backgroundColor = .lightGray 24 | 25 | let x: CGFloat = 10 26 | let width = view.frame.width - 20 27 | 28 | let scrollView = UIScrollView(frame: CGRect(x: x, y: 100, width: width, height: 200)) 29 | scrollView.contentSize = CGSize(width: width + 20, height: 200) 30 | view.addSubview(scrollView) 31 | 32 | imageView = BBAnimatedImageView(frame: scrollView.bounds) 33 | imageView.backgroundColor = .yellow 34 | imageView.contentMode = .scaleAspectFit 35 | let url = Bundle(for: self.classForCoder).url(forResource: "Rotating_earth", withExtension: "gif")! 36 | let data = try! Data(contentsOf: url) 37 | imageView.image = BBAnimatedImage(bb_data: data) 38 | scrollView.addSubview(imageView) 39 | 40 | var y = scrollView.frame.maxY + 10 41 | let height: CGFloat = 30 42 | 43 | scaleLabel = UILabel(frame: CGRect(x: x, y: y, width: width, height: height)) 44 | scaleLabel.text = "Duration scale: 1.0" 45 | scaleLabel.textAlignment = .center 46 | view.addSubview(scaleLabel) 47 | 48 | y = scaleLabel.frame.maxY 49 | 50 | let scaleSilder = UISlider(frame: CGRect(x: x + 30, y: y, width: width - 60, height: height)) 51 | scaleSilder.minimumValue = 0.2 52 | scaleSilder.maximumValue = 5 53 | scaleSilder.value = 1 54 | scaleSilder.addTarget(self, action: #selector(scaleSliderChanged(_:)), for: .valueChanged) 55 | view.addSubview(scaleSilder) 56 | 57 | y = scaleSilder.frame.maxY + 10 58 | 59 | var buttonIndex = 0 60 | let generateButton = { (title: String?, selectedTitle: String?) -> UIButton in 61 | let button = UIButton(frame: CGRect(x: x, y: y, width: self.imageView.frame.width, height: height)) 62 | button.backgroundColor = (buttonIndex % 2 == 0 ? .blue : .red) 63 | button.setTitle(title, for: .normal) 64 | button.setTitle(selectedTitle, for: .selected) 65 | self.view.addSubview(button) 66 | y = button.frame.maxY 67 | buttonIndex += 1 68 | return button 69 | } 70 | 71 | let stopButton = generateButton("Stop", "Start") 72 | stopButton.addTarget(self, action: #selector(stopButtonClicked(_:)), for: .touchUpInside) 73 | 74 | let runLoopButton = generateButton("Change to default mode", "Change to common mode") 75 | runLoopButton.addTarget(self, action: #selector(runLoopButtonClicked(_:)), for: .touchUpInside) 76 | 77 | let filterButton = generateButton("Add filter", "Remove filter") 78 | filterButton.addTarget(self, action: #selector(filterButtonClicked(_:)), for: .touchUpInside) 79 | 80 | let frameIndexButton = generateButton("Set current frame index 0", nil) 81 | frameIndexButton.addTarget(self, action: #selector(frameIndexButtonClicked(_:)), for: .touchUpInside) 82 | 83 | let changeImageSegment = UISegmentedControl(frame: CGRect(x: x, y: y, width: width, height: height)) 84 | changeImageSegment.insertSegment(withTitle: "GIF", at: 0, animated: false) 85 | changeImageSegment.insertSegment(withTitle: "Static", at: 1, animated: false) 86 | changeImageSegment.insertSegment(withTitle: "Images", at: 2, animated: false) 87 | changeImageSegment.selectedSegmentIndex = 0 88 | changeImageSegment.addTarget(self, action: #selector(changeImageSegmentChanged(_:)), for: .valueChanged) 89 | view.addSubview(changeImageSegment) 90 | } 91 | 92 | @objc private func scaleSliderChanged(_ slider: UISlider) { 93 | scaleLabel.text = String(format: "Duration scale: %.1f", slider.value) 94 | imageView.bb_animationDurationScale = Double(slider.value) 95 | } 96 | 97 | @objc private func stopButtonClicked(_ button: UIButton) { 98 | button.isSelected = !button.isSelected 99 | if button.isSelected { 100 | imageView.stopAnimating() 101 | } else { 102 | imageView.startAnimating() 103 | } 104 | } 105 | 106 | @objc private func runLoopButtonClicked(_ button: UIButton) { 107 | button.isSelected = !button.isSelected 108 | imageView.bb_runLoopMode = (button.isSelected ? .default : .common) 109 | } 110 | 111 | @objc private func filterButtonClicked(_ button: UIButton) { 112 | if editor == nil { 113 | let e = bb_imageEditorCILookupTestFilter() 114 | editor = e 115 | button.isSelected = true 116 | } else { 117 | editor = nil 118 | button.isSelected = false 119 | } 120 | if let animatedImage = imageView.image as? BBAnimatedImage { 121 | animatedImage.bb_editor = editor 122 | } 123 | } 124 | 125 | @objc private func frameIndexButtonClicked(_ button: UIButton) { 126 | imageView.bb_setCurrentFrameIndex(0, decodeIfNeeded: true) 127 | } 128 | 129 | @objc private func changeImageSegmentChanged(_ segment: UISegmentedControl) { 130 | switch segment.selectedSegmentIndex { 131 | case 0: 132 | let url = Bundle(for: self.classForCoder).url(forResource: "Rotating_earth", withExtension: "gif")! 133 | let data = try! Data(contentsOf: url) 134 | let animatedImage = BBAnimatedImage(bb_data: data) 135 | animatedImage?.bb_editor = editor 136 | imageView.image = animatedImage 137 | imageView.animationImages = nil 138 | case 1: 139 | imageView.image = UIImage(named: "placeholder") 140 | imageView.animationImages = nil 141 | case 2: 142 | imageView.animationImages = [UIImage(named: "sunflower.jpg")!, UIImage(named: "test_lookup")!] 143 | imageView.animationDuration = 1 144 | imageView.image = nil 145 | default: 146 | break 147 | } 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /BBWebImageDemo/BBWebImageDemo/TestVC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TestVC.swift 3 | // BBWebImageDemo 4 | // 5 | // Created by Kaibo Lu on 2018/10/5. 6 | // Copyright © 2018年 Kaibo Lu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import BBWebImage 11 | 12 | class TestVC: UIViewController { 13 | 14 | private var donwloader: BBMergeRequestImageDownloader! 15 | 16 | override func viewDidLoad() { 17 | super.viewDidLoad() 18 | 19 | view.backgroundColor = UIColor.lightGray 20 | 21 | let imageView = UIImageView(frame: CGRect(x: 10, y: 100, width: view.frame.width - 20, height: view.frame.height - 200)) 22 | imageView.contentMode = .scaleAspectFill 23 | imageView.clipsToBounds = true 24 | view.addSubview(imageView) 25 | 26 | let url = URL(string: "http://qzonestyle.gtimg.cn/qzone/app/weishi/client/testimage/origin/1.jpg")! 27 | let editor = bb_imageEditorCommon(with: imageView.frame.size, 28 | fillContentMode: .topLeft, 29 | maxResolution: 1024, 30 | corner: [.topLeft, .bottomRight], 31 | cornerRadius: 10, 32 | borderWidth: 2, 33 | borderColor: .yellow, 34 | backgroundColor: .blue) 35 | imageView.bb_setImage(with: url, placeholder: UIImage(named: "placeholder"), options: .none, editor: editor) { (image: UIImage?, data: Data?, error: Error?, cacheType: BBImageCacheType) in 36 | print("Completion") 37 | if let currentImage = image { 38 | print("Image: \(currentImage)") 39 | if let currentData = data { 40 | print("Data: \(currentData)") 41 | } else { 42 | print("No data") 43 | } 44 | print("Cache type: \(cacheType)") 45 | if let imageFormat = currentImage.bb_imageFormat { 46 | print("Image format: \(imageFormat)") 47 | } else { 48 | print("No image format") 49 | } 50 | } else if let currentError = error { 51 | print("Error: \(currentError)") 52 | } 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /BBWebImageDemo/BBWebImageDemo/large_leaves_70mp.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/BBWebImageDemo/BBWebImageDemo/large_leaves_70mp.jpg -------------------------------------------------------------------------------- /BBWebImageDemo/BBWebImageDemo/placeholder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/BBWebImageDemo/BBWebImageDemo/placeholder.png -------------------------------------------------------------------------------- /BBWebImageDemo/BBWebImageDemo/sunflower.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/BBWebImageDemo/BBWebImageDemo/sunflower.jpg -------------------------------------------------------------------------------- /BBWebImageDemo/BBWebImageDemo/test_lookup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/BBWebImageDemo/BBWebImageDemo/test_lookup.png -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // CompareImageLib 4 | // 5 | // Created by Kaibo Lu on 3/4/19. 6 | // Copyright © 2019 Kaibo Lu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(_ application: UIApplication) { 23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 24 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(_ application: UIApplication) { 28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(_ application: UIApplication) { 33 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(_ application: UIApplication) { 37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 38 | } 39 | 40 | func applicationWillTerminate(_ application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/BBAnimatedImageWallCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BBAnimatedImageWallCell.swift 3 | // CompareImageLib 4 | // 5 | // Created by Kaibo Lu on 3/15/19. 6 | // Copyright © 2019 Kaibo Lu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import BBWebImage 11 | 12 | class BBAnimatedImageWallCell: UICollectionViewCell { 13 | private var imageView: UIImageView! 14 | 15 | override init(frame: CGRect) { 16 | super.init(frame: frame) 17 | 18 | imageView = BBAnimatedImageView(frame: CGRect(origin: .zero, size: frame.size)) 19 | imageView.contentMode = .scaleAspectFill 20 | imageView.clipsToBounds = true 21 | contentView.addSubview(imageView) 22 | } 23 | 24 | required init?(coder aDecoder: NSCoder) { 25 | fatalError("init(coder:) has not been implemented") 26 | } 27 | 28 | func set(url: URL) { 29 | let placeholder = UIImage(named: "placeholder") 30 | imageView.bb_setImage(with: url, placeholder: placeholder) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/BBAnimatedImageWallVC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BBAnimatedImageWallVC.swift 3 | // CompareImageLib 4 | // 5 | // Created by Kaibo Lu on 3/15/19. 6 | // Copyright © 2019 Kaibo Lu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class BBAnimatedImageWallVC: UIViewController { 12 | 13 | override func viewDidLoad() { 14 | super.viewDidLoad() 15 | 16 | let layout = UICollectionViewFlowLayout() 17 | layout.minimumLineSpacing = 0 18 | layout.minimumInteritemSpacing = 0 19 | let cellWidth = view.bounds.width / 4 20 | layout.itemSize = CGSize(width: cellWidth, height: cellWidth) 21 | let colletionView = UICollectionView(frame: view.bounds, collectionViewLayout: layout) 22 | colletionView.register(BBAnimatedImageWallCell.self, forCellWithReuseIdentifier: BBAnimatedImageWallCell.description()) 23 | colletionView.backgroundColor = .white 24 | colletionView.dataSource = self 25 | view.addSubview(colletionView) 26 | } 27 | } 28 | 29 | extension BBAnimatedImageWallVC: UICollectionViewDataSource { 30 | func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { 31 | return ImageURLProvider.gifUrlStrings.count 32 | } 33 | 34 | func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { 35 | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: BBAnimatedImageWallCell.description(), for: indexPath) as! BBAnimatedImageWallCell 36 | if let url = ImageURLProvider.gifURL(forIndex: indexPath.item) { 37 | cell.set(url: url) 38 | } 39 | return cell 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/ImageWallCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImageWallCell.swift 3 | // CompareImageLib 4 | // 5 | // Created by Kaibo Lu on 3/15/19. 6 | // Copyright © 2019 Kaibo Lu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import BBWebImage 11 | import SDWebImage 12 | import YYWebImage 13 | import Kingfisher 14 | 15 | class ImageWallCell: UICollectionViewCell { 16 | private var imageView: UIImageView! 17 | 18 | override init(frame: CGRect) { 19 | super.init(frame: frame) 20 | 21 | imageView = UIImageView(frame: CGRect(origin: .zero, size: frame.size)) 22 | imageView.contentMode = .scaleAspectFill 23 | imageView.clipsToBounds = true 24 | contentView.addSubview(imageView) 25 | } 26 | 27 | required init?(coder aDecoder: NSCoder) { 28 | fatalError("init(coder:) has not been implemented") 29 | } 30 | 31 | func set(url: URL, type: TestType) { 32 | let placeholder = UIImage(named: "placeholder") 33 | switch type { 34 | case .BBWebImage: 35 | imageView.bb_setImage(with: url, placeholder: placeholder) 36 | case .SDWebImage: 37 | imageView.sd_setImage(with: url, placeholderImage: placeholder) 38 | case .YYWebImage: 39 | imageView.yy_setImage(with: url, placeholder: placeholder) 40 | case .Kingfisher: 41 | imageView.kf.setImage(with: url, placeholder: placeholder) 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/ImageWallVC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImageWallVC.swift 3 | // CompareImageLib 4 | // 5 | // Created by Kaibo Lu on 3/15/19. 6 | // Copyright © 2019 Kaibo Lu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | enum TestType { 12 | case BBWebImage 13 | case SDWebImage 14 | case YYWebImage 15 | case Kingfisher 16 | } 17 | 18 | class ImageWallVC: UIViewController { 19 | 20 | private let type: TestType 21 | 22 | init(type: TestType) { 23 | self.type = type 24 | super.init(nibName: nil, bundle: nil) 25 | } 26 | 27 | required init?(coder aDecoder: NSCoder) { 28 | fatalError("init(coder:) has not been implemented") 29 | } 30 | 31 | override func viewDidLoad() { 32 | super.viewDidLoad() 33 | 34 | let layout = UICollectionViewFlowLayout() 35 | layout.minimumLineSpacing = 0 36 | layout.minimumInteritemSpacing = 0 37 | let cellWidth = view.bounds.width / 4 38 | layout.itemSize = CGSize(width: cellWidth, height: cellWidth) 39 | let colletionView = UICollectionView(frame: view.bounds, collectionViewLayout: layout) 40 | colletionView.register(ImageWallCell.self, forCellWithReuseIdentifier: ImageWallCell.description()) 41 | colletionView.backgroundColor = .white 42 | colletionView.dataSource = self 43 | view.addSubview(colletionView) 44 | } 45 | } 46 | 47 | extension ImageWallVC: UICollectionViewDataSource { 48 | func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { 49 | return 4000 50 | } 51 | 52 | func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { 53 | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: ImageWallCell.description(), for: indexPath) as! ImageWallCell 54 | if let url = ImageURLProvider.originURL(forIndex: indexPath.item + 1) { 55 | cell.set(url: url, type: type) 56 | } 57 | return cell 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | NSAppTransportSecurity 24 | 25 | NSAllowsArbitraryLoads 26 | 27 | 28 | UILaunchStoryboardName 29 | LaunchScreen 30 | UIMainStoryboardFile 31 | Main 32 | UIRequiredDeviceCapabilities 33 | 34 | armv7 35 | 36 | UISupportedInterfaceOrientations 37 | 38 | UIInterfaceOrientationPortrait 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UISupportedInterfaceOrientations~ipad 43 | 44 | UIInterfaceOrientationPortrait 45 | UIInterfaceOrientationPortraitUpsideDown 46 | UIInterfaceOrientationLandscapeLeft 47 | UIInterfaceOrientationLandscapeRight 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/KiAnimatedImageWallCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // KiAnimatedImageWallCell.swift 3 | // CompareImageLib 4 | // 5 | // Created by Kaibo Lu on 3/15/19. 6 | // Copyright © 2019 Kaibo Lu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Kingfisher 11 | 12 | class KiAnimatedImageWallCell: UICollectionViewCell { 13 | private var imageView: UIImageView! 14 | 15 | override init(frame: CGRect) { 16 | super.init(frame: frame) 17 | 18 | imageView = AnimatedImageView(frame: CGRect(origin: .zero, size: frame.size)) 19 | imageView.contentMode = .scaleAspectFill 20 | imageView.clipsToBounds = true 21 | contentView.addSubview(imageView) 22 | } 23 | 24 | required init?(coder aDecoder: NSCoder) { 25 | fatalError("init(coder:) has not been implemented") 26 | } 27 | 28 | func set(url: URL) { 29 | let placeholder = UIImage(named: "placeholder") 30 | imageView.kf.setImage(with: url, placeholder: placeholder) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/KiAnimatedImageWallVC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // KiAnimatedImageWallVC.swift 3 | // CompareImageLib 4 | // 5 | // Created by Kaibo Lu on 3/15/19. 6 | // Copyright © 2019 Kaibo Lu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class KiAnimatedImageWallVC: UIViewController { 12 | 13 | override func viewDidLoad() { 14 | super.viewDidLoad() 15 | 16 | let layout = UICollectionViewFlowLayout() 17 | layout.minimumLineSpacing = 0 18 | layout.minimumInteritemSpacing = 0 19 | let cellWidth = view.bounds.width / 4 20 | layout.itemSize = CGSize(width: cellWidth, height: cellWidth) 21 | let colletionView = UICollectionView(frame: view.bounds, collectionViewLayout: layout) 22 | colletionView.register(KiAnimatedImageWallCell.self, forCellWithReuseIdentifier: KiAnimatedImageWallCell.description()) 23 | colletionView.backgroundColor = .white 24 | colletionView.dataSource = self 25 | view.addSubview(colletionView) 26 | } 27 | } 28 | 29 | extension KiAnimatedImageWallVC: UICollectionViewDataSource { 30 | func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { 31 | return ImageURLProvider.gifUrlStrings.count 32 | } 33 | 34 | func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { 35 | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: KiAnimatedImageWallCell.description(), for: indexPath) as! KiAnimatedImageWallCell 36 | if let url = ImageURLProvider.gifURL(forIndex: indexPath.item) { 37 | cell.set(url: url) 38 | } 39 | return cell 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/MainMenuVC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MainMenuVC.swift 3 | // CompareImageLib 4 | // 5 | // Created by Kaibo Lu on 3/4/19. 6 | // Copyright © 2019 Kaibo Lu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import BBWebImage 11 | import SDWebImage 12 | import YYWebImage 13 | import Kingfisher 14 | 15 | typealias NoParamterBlock = () -> Void 16 | 17 | class MainMenuVC: UIViewController { 18 | 19 | @IBOutlet weak var tableView: UITableView! 20 | 21 | private var list: [(String, NoParamterBlock)]! 22 | 23 | override func viewDidLoad() { 24 | super.viewDidLoad() 25 | 26 | let testBB = { [weak self] in 27 | if let self = self { self.navigationController?.pushViewController(ImageWallVC(type: .BBWebImage), animated: true) } 28 | } 29 | let testSD = { [weak self] in 30 | if let self = self { self.navigationController?.pushViewController(ImageWallVC(type: .SDWebImage), animated: true) } 31 | } 32 | let testYY = { [weak self] in 33 | if let self = self { self.navigationController?.pushViewController(ImageWallVC(type: .YYWebImage), animated: true) } 34 | } 35 | let testKi = { [weak self] in 36 | if let self = self { self.navigationController?.pushViewController(ImageWallVC(type: .Kingfisher), animated: true) } 37 | } 38 | let testGifBB = { [weak self] in 39 | if let self = self { self.navigationController?.pushViewController(BBAnimatedImageWallVC(), animated: true) } 40 | } 41 | let testGifSD = { [weak self] in 42 | if let self = self { self.navigationController?.pushViewController(SDAnimatedImageWallVC(), animated: true) } 43 | } 44 | let testGifYY = { [weak self] in 45 | if let self = self { self.navigationController?.pushViewController(YYAnimatedImageWallVC(), animated: true) } 46 | } 47 | let testGifKi = { [weak self] in 48 | if let self = self { self.navigationController?.pushViewController(KiAnimatedImageWallVC(), animated: true) } 49 | } 50 | let testCache = { [weak self] in 51 | if let self = self { self.navigationController?.pushViewController(TestCacheVC(), animated: true) } 52 | } 53 | let clear = { [weak self] in 54 | guard self != nil else { return } 55 | BBWebImageManager.shared.imageCache.clear(.all, completion: nil) 56 | 57 | SDWebImageManager.shared().imageCache?.clearMemory() 58 | SDWebImageManager.shared().imageCache?.clearDisk(onCompletion: nil) 59 | 60 | YYWebImageManager.shared().cache?.memoryCache.removeAllObjects() 61 | YYWebImageManager.shared().cache?.diskCache.removeAllObjects() 62 | 63 | KingfisherManager.shared.cache.clearMemoryCache() 64 | KingfisherManager.shared.cache.clearDiskCache() 65 | } 66 | list = [("BBWebImage image wall", testBB), 67 | ("SDWebImage image wall", testSD), 68 | ("YYWebImage image wall", testYY), 69 | ("Kingfisher image wall", testKi), 70 | ("BBWebImage GIF wall", testGifBB), 71 | ("SDWebImage GIF wall", testGifSD), 72 | ("YYWebImage GIF wall", testGifYY), 73 | ("Kingfisher GIF wall", testGifKi), 74 | ("Test cache", testCache), 75 | ("Clear", clear)] 76 | 77 | tableView.register(UITableViewCell.self, forCellReuseIdentifier: UITableViewCell.description()) 78 | tableView.dataSource = self 79 | tableView.delegate = self 80 | } 81 | } 82 | 83 | extension MainMenuVC: UITableViewDataSource { 84 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 85 | return list.count 86 | } 87 | 88 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 89 | let cell = tableView.dequeueReusableCell(withIdentifier: UITableViewCell.description(), for: indexPath) 90 | cell.textLabel?.text = list[indexPath.row].0 91 | return cell 92 | } 93 | } 94 | 95 | extension MainMenuVC: UITableViewDelegate { 96 | func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 97 | tableView.deselectRow(at: indexPath, animated: true) 98 | list[indexPath.row].1() 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/SDAnimatedImageWallCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SDAnimatedImageWallCell.swift 3 | // CompareImageLib 4 | // 5 | // Created by Kaibo Lu on 3/15/19. 6 | // Copyright © 2019 Kaibo Lu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import SDWebImage 11 | 12 | class SDAnimatedImageWallCell: UICollectionViewCell { 13 | private var imageView: FLAnimatedImageView! 14 | 15 | override init(frame: CGRect) { 16 | super.init(frame: frame) 17 | 18 | imageView = FLAnimatedImageView(frame: CGRect(origin: .zero, size: frame.size)) 19 | imageView.contentMode = .scaleAspectFill 20 | imageView.clipsToBounds = true 21 | contentView.addSubview(imageView) 22 | } 23 | 24 | required init?(coder aDecoder: NSCoder) { 25 | fatalError("init(coder:) has not been implemented") 26 | } 27 | 28 | func set(url: URL) { 29 | let placeholder = UIImage(named: "placeholder") 30 | imageView.sd_setImage(with: url, placeholderImage: placeholder, options: [], completed: nil) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/SDAnimatedImageWallVC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SDAnimatedImageWallVC.swift 3 | // CompareImageLib 4 | // 5 | // Created by Kaibo Lu on 3/15/19. 6 | // Copyright © 2019 Kaibo Lu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class SDAnimatedImageWallVC: UIViewController { 12 | 13 | override func viewDidLoad() { 14 | super.viewDidLoad() 15 | 16 | let layout = UICollectionViewFlowLayout() 17 | layout.minimumLineSpacing = 0 18 | layout.minimumInteritemSpacing = 0 19 | let cellWidth = view.bounds.width / 4 20 | layout.itemSize = CGSize(width: cellWidth, height: cellWidth) 21 | let colletionView = UICollectionView(frame: view.bounds, collectionViewLayout: layout) 22 | colletionView.register(SDAnimatedImageWallCell.self, forCellWithReuseIdentifier: SDAnimatedImageWallCell.description()) 23 | colletionView.backgroundColor = .white 24 | colletionView.dataSource = self 25 | view.addSubview(colletionView) 26 | } 27 | } 28 | 29 | extension SDAnimatedImageWallVC: UICollectionViewDataSource { 30 | func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { 31 | return ImageURLProvider.gifUrlStrings.count 32 | } 33 | 34 | func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { 35 | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: SDAnimatedImageWallCell.description(), for: indexPath) as! SDAnimatedImageWallCell 36 | if let url = ImageURLProvider.gifURL(forIndex: indexPath.item) { 37 | cell.set(url: url) 38 | } 39 | return cell 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_1.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_10.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_10.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_100.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_100.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_100_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_100_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_10_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_10_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_11.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_11.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_11_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_11_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_12.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_12.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_12_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_12_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_13.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_13.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_13_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_13_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_14.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_14.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_14_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_14_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_15.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_15.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_15_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_15_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_16.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_16.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_16_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_16_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_17.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_17.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_17_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_17_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_18.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_18.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_18_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_18_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_19.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_19.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_19_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_19_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_1_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_1_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_2.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_20.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_20.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_20_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_20_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_21.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_21.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_21_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_21_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_22.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_22.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_22_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_22_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_23.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_23.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_23_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_23_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_24.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_24.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_24_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_24_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_25.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_25.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_25_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_25_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_26.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_26.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_26_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_26_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_27.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_27.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_27_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_27_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_28.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_28.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_28_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_28_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_29.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_29.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_29_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_29_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_2_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_2_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_3.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_30.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_30.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_30_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_30_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_31.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_31.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_31_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_31_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_32.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_32.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_32_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_32_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_33.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_33.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_33_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_33_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_34.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_34.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_34_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_34_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_35.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_35.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_35_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_35_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_36.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_36.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_36_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_36_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_37.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_37.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_37_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_37_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_38.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_38.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_38_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_38_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_39.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_39.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_39_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_39_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_3_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_3_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_4.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_40.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_40.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_40_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_40_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_41.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_41.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_41_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_41_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_42.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_42.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_42_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_42_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_43.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_43.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_43_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_43_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_44.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_44.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_44_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_44_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_45.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_45.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_45_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_45_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_46.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_46.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_46_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_46_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_47.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_47.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_47_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_47_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_48.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_48.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_48_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_48_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_49.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_49.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_49_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_49_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_4_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_4_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_5.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_50.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_50.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_50_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_50_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_51.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_51.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_51_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_51_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_52.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_52.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_52_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_52_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_53.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_53.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_53_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_53_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_54.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_54.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_54_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_54_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_55.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_55.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_55_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_55_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_56.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_56.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_56_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_56_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_57.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_57.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_57_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_57_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_58.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_58.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_58_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_58_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_59.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_59.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_59_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_59_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_5_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_5_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_6.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_60.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_60.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_60_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_60_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_61.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_61.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_61_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_61_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_62.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_62.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_62_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_62_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_63.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_63.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_63_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_63_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_64_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_64_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_65.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_65.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_65_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_65_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_66.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_66.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_66_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_66_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_67.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_67.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_67_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_67_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_68.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_68.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_68_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_68_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_69.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_69.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_69_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_69_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_6_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_6_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_7.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_70.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_70.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_70_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_70_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_71.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_71.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_71_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_71_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_72.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_72.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_72_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_72_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_73.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_73.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_73_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_73_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_74.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_74.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_74_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_74_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_75.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_75.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_75_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_75_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_76.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_76.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_76_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_76_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_77.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_77.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_77_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_77_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_78.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_78.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_78_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_78_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_79.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_79.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_79_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_79_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_7_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_7_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_8.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_80.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_80.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_80_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_80_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_81.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_81.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_81_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_81_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_82.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_82.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_82_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_82_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_83.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_83.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_83_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_83_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_84.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_84.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_84_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_84_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_85.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_85.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_85_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_85_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_86.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_86.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_86_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_86_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_87.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_87.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_87_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_87_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_88.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_88.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_88_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_88_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_89.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_89.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_89_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_89_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_8_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_8_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_9.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_90.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_90.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_90_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_90_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_91.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_91.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_91_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_91_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_92.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_92.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_92_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_92_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_93.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_93.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_93_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_93_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_94.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_94.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_94_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_94_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_95.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_95.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_95_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_95_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_96.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_96.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_96_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_96_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_97.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_97.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_97_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_97_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_98.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_98.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_98_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_98_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_99.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_99.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_99_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_99_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/TestImages/test_9_size_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/CompareImageLib/CompareImageLib/TestImages/test_9_size_64.jpg -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/YYAnimatedImageWallCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // YYAnimatedImageWallCell.swift 3 | // CompareImageLib 4 | // 5 | // Created by Kaibo Lu on 3/15/19. 6 | // Copyright © 2019 Kaibo Lu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import YYWebImage 11 | 12 | class YYAnimatedImageWallCell: UICollectionViewCell { 13 | private var imageView: UIImageView! 14 | 15 | override init(frame: CGRect) { 16 | super.init(frame: frame) 17 | 18 | imageView = YYAnimatedImageView(frame: CGRect(origin: .zero, size: frame.size)) 19 | imageView.contentMode = .scaleAspectFill 20 | imageView.clipsToBounds = true 21 | contentView.addSubview(imageView) 22 | } 23 | 24 | required init?(coder aDecoder: NSCoder) { 25 | fatalError("init(coder:) has not been implemented") 26 | } 27 | 28 | func set(url: URL) { 29 | let placeholder = UIImage(named: "placeholder") 30 | imageView.yy_setImage(with: url, placeholder: placeholder) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /CompareImageLib/CompareImageLib/YYAnimatedImageWallVC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // YYAnimatedImageWallVC.swift 3 | // CompareImageLib 4 | // 5 | // Created by Kaibo Lu on 3/15/19. 6 | // Copyright © 2019 Kaibo Lu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class YYAnimatedImageWallVC: UIViewController { 12 | 13 | override func viewDidLoad() { 14 | super.viewDidLoad() 15 | 16 | let layout = UICollectionViewFlowLayout() 17 | layout.minimumLineSpacing = 0 18 | layout.minimumInteritemSpacing = 0 19 | let cellWidth = view.bounds.width / 4 20 | layout.itemSize = CGSize(width: cellWidth, height: cellWidth) 21 | let colletionView = UICollectionView(frame: view.bounds, collectionViewLayout: layout) 22 | colletionView.register(YYAnimatedImageWallCell.self, forCellWithReuseIdentifier: YYAnimatedImageWallCell.description()) 23 | colletionView.backgroundColor = .white 24 | colletionView.dataSource = self 25 | view.addSubview(colletionView) 26 | } 27 | } 28 | 29 | extension YYAnimatedImageWallVC: UICollectionViewDataSource { 30 | func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { 31 | return ImageURLProvider.gifUrlStrings.count 32 | } 33 | 34 | func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { 35 | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: YYAnimatedImageWallCell.description(), for: indexPath) as! YYAnimatedImageWallCell 36 | if let url = ImageURLProvider.gifURL(forIndex: indexPath.item) { 37 | cell.set(url: url) 38 | } 39 | return cell 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /CompareImageLib/Podfile: -------------------------------------------------------------------------------- 1 | platform :ios, '8.0' 2 | 3 | target 'CompareImageLib' do 4 | # Comment the next line if you're not using Swift and don't want to use dynamic frameworks 5 | use_frameworks! 6 | 7 | pod 'Kingfisher' 8 | pod 'SDWebImage/GIF' 9 | pod 'YYWebImage' 10 | 11 | end 12 | -------------------------------------------------------------------------------- /CompareImageLib/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - FLAnimatedImage (1.0.12) 3 | - Kingfisher (4.10.1) 4 | - SDWebImage/Core (4.4.6) 5 | - SDWebImage/GIF (4.4.6): 6 | - FLAnimatedImage (~> 1.0) 7 | - SDWebImage/Core 8 | - YYCache (1.0.4) 9 | - YYImage (1.0.4): 10 | - YYImage/Core (= 1.0.4) 11 | - YYImage/Core (1.0.4) 12 | - YYWebImage (1.0.5): 13 | - YYCache 14 | - YYImage 15 | 16 | DEPENDENCIES: 17 | - Kingfisher 18 | - SDWebImage/GIF 19 | - YYWebImage 20 | 21 | SPEC REPOS: 22 | https://github.com/cocoapods/specs.git: 23 | - FLAnimatedImage 24 | - Kingfisher 25 | - SDWebImage 26 | - YYCache 27 | - YYImage 28 | - YYWebImage 29 | 30 | SPEC CHECKSUMS: 31 | FLAnimatedImage: 4a0b56255d9b05f18b6dd7ee06871be5d3b89e31 32 | Kingfisher: c148cd7b47ebde9989f6bc7c27dcaa79d81279a0 33 | SDWebImage: 3f3f0c02f09798048c47a5ed0a13f17b063572d8 34 | YYCache: 8105b6638f5e849296c71f331ff83891a4942952 35 | YYImage: 1e1b62a9997399593e4b9c4ecfbbabbf1d3f3b54 36 | YYWebImage: 5f7f36aee2ae293f016d418c7d6ba05c4863e928 37 | 38 | PODFILE CHECKSUM: 49900334799b07b34941a9a531723dc747bdc421 39 | 40 | COCOAPODS: 1.5.3 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Kaibo Lu 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README_resources/CompareImageLib.key: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/README_resources/CompareImageLib.key -------------------------------------------------------------------------------- /README_resources/CompareImageLib.numbers: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/README_resources/CompareImageLib.numbers -------------------------------------------------------------------------------- /README_resources/architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/README_resources/architecture.png -------------------------------------------------------------------------------- /README_resources/compare_diskCache.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/README_resources/compare_diskCache.png -------------------------------------------------------------------------------- /README_resources/compare_gif_CPU_memory.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/README_resources/compare_gif_CPU_memory.png -------------------------------------------------------------------------------- /README_resources/compare_memoryCache.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/README_resources/compare_memoryCache.png -------------------------------------------------------------------------------- /README_resources/edit_common.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/README_resources/edit_common.gif -------------------------------------------------------------------------------- /README_resources/edit_filter.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/README_resources/edit_filter.gif -------------------------------------------------------------------------------- /README_resources/original_image.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silence-GitHub/BBWebImage/66b6531a2b31bd313b4e0f313d97c189904e5f1c/README_resources/original_image.gif --------------------------------------------------------------------------------