mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-10-09 03:20:48 +00:00
Optimize image loading
This commit is contained in:
parent
eef25b4070
commit
aebdefc477
@ -26,6 +26,7 @@ private final class MediaPreviewView: UIView {
|
||||
self.media = media
|
||||
|
||||
self.imageView = TransformImageView()
|
||||
self.imageView.contentAnimations = .subsequentUpdates
|
||||
|
||||
super.init(frame: CGRect())
|
||||
|
||||
|
@ -183,16 +183,24 @@ public final class DirectMediaImageCache {
|
||||
return self.account.postbox.mediaBox.cachedRepresentationPathForId(resourceId.stringRepresentation, representationId: representationId, keepDuration: .general)
|
||||
}
|
||||
|
||||
private func getLoadSignal(resource: MediaResourceReference, width: Int) -> Signal<UIImage?, NoError>? {
|
||||
let cachePath = self.getCachePath(resourceId: resource.resource.id, imageType: .square(width: width))
|
||||
private func getLoadSignal(width: Int, resource: MediaResourceReference, resourceSizeLimit: Int) -> Signal<UIImage?, NoError>? {
|
||||
return Signal { subscriber in
|
||||
let fetch = fetchedMediaResource(mediaBox: self.account.postbox.mediaBox, reference: resource).start()
|
||||
let data = (self.account.postbox.mediaBox.resourceData(resource.resource)
|
||||
|> filter { data in
|
||||
return data.complete
|
||||
}
|
||||
|> take(1)).start(next: { data in
|
||||
if let dataValue = try? Data(contentsOf: URL(fileURLWithPath: data.path)), let image = UIImage(data: dataValue) {
|
||||
let cachePath = self.getCachePath(resourceId: resource.resource.id, imageType: .square(width: width))
|
||||
|
||||
let fetch = fetchedMediaResource(
|
||||
mediaBox: self.account.postbox.mediaBox,
|
||||
reference: resource,
|
||||
ranges: [(0 ..< resourceSizeLimit, .default)],
|
||||
statsCategory: .image,
|
||||
reportResultStatus: false,
|
||||
preferBackgroundReferenceRevalidation: false,
|
||||
continueInBackground: false
|
||||
).start()
|
||||
|
||||
let data = self.account.postbox.mediaBox.resourceData(resource.resource, size: resourceSizeLimit, in: 0 ..< resourceSizeLimit).start(next: { data, _ in
|
||||
let dataValue = data
|
||||
|
||||
if let image = UIImage(data: dataValue) {
|
||||
let scaledSize = CGSize(width: CGFloat(width), height: CGFloat(width))
|
||||
let scaledContext = DrawingContext(size: scaledSize, scale: 1.0, opaque: true)
|
||||
scaledContext.withFlippedContext { context in
|
||||
@ -215,94 +223,99 @@ public final class DirectMediaImageCache {
|
||||
}
|
||||
}
|
||||
|
||||
private func getResource(message: Message, image: TelegramMediaImage) -> MediaResourceReference? {
|
||||
guard let representation = image.representations.last else {
|
||||
return nil
|
||||
}
|
||||
return MediaReference.message(message: MessageReference(message), media: image).resourceReference(representation.resource)
|
||||
}
|
||||
|
||||
private func getResource(message: Message, file: TelegramMediaFile) -> MediaResourceReference? {
|
||||
if let representation = file.previewRepresentations.last {
|
||||
return MediaReference.message(message: MessageReference(message), media: file).resourceReference(representation.resource)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
public func getImage(message: Message, media: Media, width: Int, synchronous: Bool) -> GetMediaResult? {
|
||||
if synchronous {
|
||||
var immediateThumbnailData: Data?
|
||||
var resource: MediaResourceReference?
|
||||
if let image = media as? TelegramMediaImage {
|
||||
immediateThumbnailData = image.immediateThumbnailData
|
||||
resource = self.getResource(message: message, image: image)
|
||||
} else if let file = media as? TelegramMediaFile {
|
||||
immediateThumbnailData = file.immediateThumbnailData
|
||||
resource = self.getResource(message: message, file: file)
|
||||
private func getProgressiveSize(mediaReference: AnyMediaReference, width: Int, representations: [TelegramMediaImageRepresentation]) -> (resource: MediaResourceReference, size: Int)? {
|
||||
if let representation = representations.first(where: { !$0.progressiveSizes.isEmpty }) {
|
||||
let selectedSize: Int32
|
||||
let progressiveSizes = representation.progressiveSizes
|
||||
if progressiveSizes.count > 0 && width <= 64 {
|
||||
selectedSize = progressiveSizes[0]
|
||||
} else if progressiveSizes.count > 1 && width <= 160 {
|
||||
selectedSize = progressiveSizes[2]
|
||||
} else if progressiveSizes.count > 2 && width <= 400 {
|
||||
selectedSize = progressiveSizes[3]
|
||||
} else {
|
||||
selectedSize = Int32.max
|
||||
}
|
||||
return (mediaReference.resourceReference(representation.resource), Int(selectedSize))
|
||||
} else {
|
||||
for representation in representations.sorted(by: { $0.dimensions.width < $1.dimensions.width }) {
|
||||
if Int(Float(representation.dimensions.width) * 1.2) >= width {
|
||||
return (mediaReference.resourceReference(representation.resource), Int(Int32.max))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
if let resource = resource {
|
||||
if let data = try? Data(contentsOf: URL(fileURLWithPath: self.getCachePath(resourceId: resource.resource.id, imageType: .square(width: width)))), let image = loadImage(data: data) {
|
||||
private func getResource(message: Message, image: TelegramMediaImage, width: Int) -> (resource: MediaResourceReference, size: Int)? {
|
||||
return self.getProgressiveSize(mediaReference: MediaReference.message(message: MessageReference(message), media: image).abstract, width: width, representations: image.representations)
|
||||
}
|
||||
|
||||
private func getResource(message: Message, file: TelegramMediaFile, width: Int) -> (resource: MediaResourceReference, size: Int)? {
|
||||
return self.getProgressiveSize(mediaReference: MediaReference.message(message: MessageReference(message), media: file).abstract, width: width, representations: file.previewRepresentations)
|
||||
}
|
||||
|
||||
private func getImageSynchronous(message: Message, media: Media, width: Int, possibleWidths: [Int]) -> GetMediaResult? {
|
||||
var immediateThumbnailData: Data?
|
||||
var resource: (resource: MediaResourceReference, size: Int)?
|
||||
if let image = media as? TelegramMediaImage {
|
||||
immediateThumbnailData = image.immediateThumbnailData
|
||||
resource = self.getResource(message: message, image: image, width: width)
|
||||
} else if let file = media as? TelegramMediaFile {
|
||||
immediateThumbnailData = file.immediateThumbnailData
|
||||
resource = self.getResource(message: message, file: file, width: width)
|
||||
}
|
||||
|
||||
guard let resource = resource else {
|
||||
return nil
|
||||
}
|
||||
|
||||
var blurredImage: UIImage?
|
||||
for otherWidth in possibleWidths.reversed() {
|
||||
if otherWidth == width {
|
||||
if let data = try? Data(contentsOf: URL(fileURLWithPath: self.getCachePath(resourceId: resource.resource.resource.id, imageType: .square(width: otherWidth)))), let image = loadImage(data: data) {
|
||||
return GetMediaResult(image: image, loadSignal: nil)
|
||||
}
|
||||
|
||||
var blurredImage: UIImage?
|
||||
if let data = try? Data(contentsOf: URL(fileURLWithPath: self.getCachePath(resourceId: resource.resource.id, imageType: .blurredThumbnail))), let image = loadImage(data: data) {
|
||||
blurredImage = image
|
||||
} else if let data = immediateThumbnailData.flatMap(decodeTinyThumbnail), let image = loadImage(data: data) {
|
||||
if let blurredImageValue = generateBlurredThumbnail(image: image) {
|
||||
blurredImage = blurredImageValue
|
||||
}
|
||||
}
|
||||
|
||||
return GetMediaResult(image: blurredImage, loadSignal: self.getLoadSignal(resource: resource, width: width))
|
||||
} else {
|
||||
return nil
|
||||
if let data = try? Data(contentsOf: URL(fileURLWithPath: self.getCachePath(resourceId: resource.resource.resource.id, imageType: .square(width: otherWidth)))), let image = loadImage(data: data) {
|
||||
blurredImage = image
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if blurredImage == nil {
|
||||
if let data = try? Data(contentsOf: URL(fileURLWithPath: self.getCachePath(resourceId: resource.resource.resource.id, imageType: .blurredThumbnail))), let image = loadImage(data: data) {
|
||||
blurredImage = image
|
||||
} else if let data = immediateThumbnailData.flatMap(decodeTinyThumbnail), let image = loadImage(data: data) {
|
||||
if let blurredImageValue = generateBlurredThumbnail(image: image) {
|
||||
blurredImage = blurredImageValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return GetMediaResult(image: blurredImage, loadSignal: self.getLoadSignal(width: width, resource: resource.resource, resourceSizeLimit: resource.size))
|
||||
}
|
||||
|
||||
public func getImage(message: Message, media: Media, width: Int, possibleWidths: [Int], synchronous: Bool) -> GetMediaResult? {
|
||||
if synchronous {
|
||||
return self.getImageSynchronous(message: message, media: media, width: width, possibleWidths: possibleWidths)
|
||||
} else {
|
||||
return GetMediaResult(image: nil, loadSignal: Signal { subscriber in
|
||||
var immediateThumbnailData: Data?
|
||||
var resource: MediaResourceReference?
|
||||
if let image = media as? TelegramMediaImage {
|
||||
immediateThumbnailData = image.immediateThumbnailData
|
||||
resource = self.getResource(message: message, image: image)
|
||||
} else if let file = media as? TelegramMediaFile {
|
||||
immediateThumbnailData = file.immediateThumbnailData
|
||||
resource = self.getResource(message: message, file: file)
|
||||
let result = self.getImageSynchronous(message: message, media: media, width: width, possibleWidths: possibleWidths)
|
||||
guard let result = result else {
|
||||
subscriber.putNext(nil)
|
||||
subscriber.putCompletion()
|
||||
|
||||
return EmptyDisposable
|
||||
}
|
||||
|
||||
if let resource = resource {
|
||||
if let data = try? Data(contentsOf: URL(fileURLWithPath: self.getCachePath(resourceId: resource.resource.id, imageType: .square(width: width)))), let image = loadImage(data: data) {
|
||||
subscriber.putNext(image)
|
||||
subscriber.putCompletion()
|
||||
if let image = result.image {
|
||||
subscriber.putNext(image)
|
||||
}
|
||||
|
||||
return EmptyDisposable
|
||||
}
|
||||
|
||||
var blurredImage: UIImage?
|
||||
if let data = try? Data(contentsOf: URL(fileURLWithPath: self.getCachePath(resourceId: resource.resource.id, imageType: .blurredThumbnail))), let image = loadImage(data: data) {
|
||||
blurredImage = image
|
||||
} else if let data = immediateThumbnailData.flatMap(decodeTinyThumbnail), let image = loadImage(data: data) {
|
||||
if let blurredImageValue = generateBlurredThumbnail(image: image) {
|
||||
blurredImage = blurredImageValue
|
||||
}
|
||||
}
|
||||
|
||||
if let blurredImage = blurredImage {
|
||||
subscriber.putNext(blurredImage)
|
||||
}
|
||||
|
||||
if let signal = self.getLoadSignal(resource: resource, width: width) {
|
||||
return signal.start(next: subscriber.putNext, completed: subscriber.putCompletion)
|
||||
} else {
|
||||
subscriber.putNext(nil)
|
||||
subscriber.putCompletion()
|
||||
|
||||
return EmptyDisposable
|
||||
}
|
||||
if let signal = result.loadSignal {
|
||||
return signal.start(next: subscriber.putNext, error: subscriber.putError, completed: subscriber.putCompletion)
|
||||
} else {
|
||||
subscriber.putNext(nil)
|
||||
subscriber.putCompletion()
|
||||
|
||||
return EmptyDisposable
|
||||
|
@ -1109,6 +1109,16 @@ private final class SparseItemGridBindingImpl: SparseItemGridBinding, ListShimme
|
||||
return nil
|
||||
}
|
||||
|
||||
private static let widthSpecs: ([Int], [Int]) = {
|
||||
let list: [(Int, Int)] = [
|
||||
(50, 64),
|
||||
(100, 150),
|
||||
(140, 200),
|
||||
(Int.max, 280)
|
||||
]
|
||||
return (list.map(\.0), list.map(\.1))
|
||||
}()
|
||||
|
||||
func bindLayers(items: [SparseItemGrid.Item], layers: [SparseItemGridDisplayItem], synchronous: Bool) {
|
||||
for i in 0 ..< items.count {
|
||||
guard let item = items[i] as? VisualMediaItem else {
|
||||
@ -1138,15 +1148,12 @@ private final class SparseItemGridBindingImpl: SparseItemGridBinding, ListShimme
|
||||
continue
|
||||
}
|
||||
|
||||
let imageWidthSpec: Int
|
||||
if layer.bounds.width <= 50 {
|
||||
imageWidthSpec = 64
|
||||
} else if layer.bounds.width <= 100 {
|
||||
imageWidthSpec = 150
|
||||
} else if layer.bounds.width <= 140 {
|
||||
imageWidthSpec = 200
|
||||
} else {
|
||||
imageWidthSpec = 280
|
||||
var imageWidthSpec: Int = SparseItemGridBindingImpl.widthSpecs.1[0]
|
||||
for i in 0 ..< SparseItemGridBindingImpl.widthSpecs.0.count {
|
||||
if Int(layer.bounds.width) <= SparseItemGridBindingImpl.widthSpecs.0[i] {
|
||||
imageWidthSpec = SparseItemGridBindingImpl.widthSpecs.1[i]
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
let message = item.message
|
||||
@ -1163,7 +1170,7 @@ private final class SparseItemGridBindingImpl: SparseItemGridBinding, ListShimme
|
||||
}
|
||||
|
||||
if let selectedMedia = selectedMedia {
|
||||
if let result = directMediaImageCache.getImage(message: message, media: selectedMedia, width: imageWidthSpec, synchronous: synchronous) {
|
||||
if let result = directMediaImageCache.getImage(message: message, media: selectedMedia, width: imageWidthSpec, possibleWidths: SparseItemGridBindingImpl.widthSpecs.1, synchronous: synchronous) {
|
||||
if let image = result.image {
|
||||
layer.contents = image.cgImage
|
||||
layer.hasContents = true
|
||||
|
Loading…
x
Reference in New Issue
Block a user